diff --git a/Builds/CMake/CMakeFuncs.cmake b/Builds/CMake/CMakeFuncs.cmake
index 87f2ee26756..690f54e8b01 100644
--- a/Builds/CMake/CMakeFuncs.cmake
+++ b/Builds/CMake/CMakeFuncs.cmake
@@ -517,20 +517,28 @@ endmacro()
macro(setup_build_boilerplate)
if (NOT WIN32 AND san)
- add_compile_options(-fsanitize=${san} -fno-omit-frame-pointer)
+ STRING(REPLACE ";" "," comma_san "${san}")
+ add_compile_options(-fsanitize=${comma_san} -fno-omit-frame-pointer)
append_flags(CMAKE_EXE_LINKER_FLAGS
- -fsanitize=${san})
+ -fsanitize=${comma_san})
- string(TOLOWER ${san} ci_san)
- if (${ci_san} STREQUAL address)
- set(SANITIZER_LIBRARIES asan)
- add_definitions(-DSANITIZER=ASAN)
- endif()
- if (${ci_san} STREQUAL thread)
- set(SANITIZER_LIBRARIES tsan)
- add_definitions(-DSANITIZER=TSAN)
- endif()
+ foreach(cur_san ${san})
+ string(TOLOWER ${cur_san} ci_san)
+ if (${ci_san} STREQUAL address)
+ # set(SANITIZER_LIBRARIES asan)
+ add_definitions(-DSANITIZER=ASAN)
+ endif()
+ if (${ci_san} STREQUAL thread)
+ # set(SANITIZER_LIBRARIES tsan)
+ add_definitions(-DSANITIZER=TSAN)
+ endif()
+ if (${ci_san} STREQUAL fuzzer)
+ add_compile_options(-fsanitize-coverage=trace-pc-guard)
+ append_flags(CMAKE_EXE_LINKER_FLAGS
+ -fsanitize-coverage=trace-pc-guard)
+ endif()
+ endforeach()
endif()
if (perf)
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index d73f5dc2c35..1febc28a88c 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1847,6 +1847,28 @@
True
True
+
+
+
+ True
+ True
+
+
+
+
+
+
+ True
+ True
+
+
+
+
+ True
+ True
+
+
+
True
True
@@ -1857,9 +1879,31 @@
True
True
+
+ True
+ True
+
+
+
+
+ True
+ True
+
-
+
+ True
+ True
+
+
+
+
+ True
+ True
+
+
+
+
True
@@ -4471,6 +4515,42 @@
True
True
+
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+ True
+ True
+
+
+
+
+ True
+ True
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index 326781e37c7..41ac5a06101 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -2490,6 +2490,30 @@
ripple\conditions\impl
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
ripple\conditions\impl
@@ -2499,12 +2523,33 @@
ripple\conditions\impl
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
ripple\conditions\impl
-
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
+ ripple\conditions\impl
+
+
ripple\conditions\impl
+
+ ripple\conditions
+
ripple\consensus
@@ -5235,6 +5280,36 @@
test\beast
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
+
+ test\conditions
+
test\conditions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7d648978bd1..7df32c7f302 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -465,16 +465,18 @@ if (WIN32 OR is_xcode)
endif()
if(unity)
- add_executable(rippled ${rippled_src_unity} ${PROTO_HDRS})
- add_executable(rippled_classic EXCLUDE_FROM_ALL ${rippled_src_nonunity} ${PROTO_HDRS})
- set(other_target rippled_classic)
+ add_executable(rippled ${rippled_src_unity} ${PROTO_HDRS})
+ add_executable(rippled_classic EXCLUDE_FROM_ALL ${rippled_src_nonunity} ${PROTO_HDRS})
+ set(other_target rippled_classic)
else()
- add_executable(rippled ${rippled_src_nonunity} ${PROTO_HDRS})
- add_executable(rippled_unity EXCLUDE_FROM_ALL ${rippled_src_unity} ${PROTO_HDRS})
- set(other_target rippled_unity)
+ add_executable(rippled ${rippled_src_nonunity} ${PROTO_HDRS})
+ add_executable(rippled_unity EXCLUDE_FROM_ALL ${rippled_src_unity} ${PROTO_HDRS})
+ set(other_target rippled_unity)
endif()
+
list(APPEND targets "rippled")
list(APPEND targets ${other_target})
+
# Not the same as EXCLUDE_FROM_ALL. Prevents Visual Studio from building the
# other_target when the user builds the solution (default when pressing )
set_property(TARGET ${other_target} PROPERTY EXCLUDE_FROM_DEFAULT_BUILD true)
@@ -530,11 +532,45 @@ set_startup_project(rippled)
foreach(target IN LISTS targets)
target_link_libraries(${target}
- ${OPENSSL_LIBRARIES} ${PROTOBUF_LIBRARIES} ${SANITIZER_LIBRARIES})
+ ${OPENSSL_LIBRARIES} ${PROTOBUF_LIBRARIES})
link_common_libraries(${target})
endforeach()
+if (fuzzer_conditions)
+ set(FuzzerSrc
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/Condition.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/DerCommon.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/DerTraits.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/DerCoder.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/Ed25519.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/error.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/Fulfillment.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/PrefixSha256.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/PreimageSha256.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/RsaSha256.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/conditions/impl/ThresholdSha256.cpp
+
+ ${CMAKE_SOURCE_DIR}/src/ripple/basics/impl/contract.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/basics/impl/Log.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/beast/utility/src/beast_Journal.cpp
+ ${CMAKE_SOURCE_DIR}/src/ripple/basics/impl/Time.cpp
+ ${CMAKE_SOURCE_DIR}/src/fuzzers/Conditions_fuzz_test.cpp)
+
+ add_with_props(FuzzerSrc src/ripple/unity/ed25519_donna.c
+ -I"${CMAKE_SOURCE_DIR}/"src/ed25519-donna)
+
+ add_executable(fulfillment_fuzzer ${FuzzerSrc})
+ target_link_libraries(fulfillment_fuzzer ${OPENSSL_LIBRARIES})
+ link_common_libraries(fulfillment_fuzzer)
+ target_compile_definitions(fulfillment_fuzzer PUBLIC -DFUZZ_TEST_FULFILLMENT)
+
+ add_executable(condition_fuzzer ${FuzzerSrc})
+ target_link_libraries(condition_fuzzer ${OPENSSL_LIBRARIES})
+ link_common_libraries(condition_fuzzer)
+ target_compile_definitions(condition_fuzzer PUBLIC -DFUZZ_TEST_CONDITION)
+endif()
+
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(WARNING "Rippled requires a 64 bit target architecture.\n"
"The most likely cause of this warning is trying to build rippled with a 32-bit OS.")
diff --git a/bin/crypto_conditions_test_gen/conditions.py b/bin/crypto_conditions_test_gen/conditions.py
new file mode 100755
index 00000000000..f17d08ba058
--- /dev/null
+++ b/bin/crypto_conditions_test_gen/conditions.py
@@ -0,0 +1,1294 @@
+#!/usr/bin/env python
+
+# This script generates two related sets of output:
+#
+# 1) c++ code used to test cryptocondtions. To generate this code, run with the
+# `--prefix ` switch. The results of the script will be put
+# in a set of files with names for the individual cryptoconditions. For
+# example: `--prefix Conditions_generated_test_` will result in cpp files for
+# Conditions_generated_test_ed.cpp, Conditions_generated_test_thresh, ect. Note
+# these generated files are not well formatted. It is useful to run them
+# through a formatter (such as clang-format). This is done outside of this
+# script.
+#
+# 2) A corpus used for fuzz testing. To generate the corpus, run with the
+# `--fuzz ` switch. The results of the script will be put in the
+# . It will create two subdirectories under , one for
+# conditions and one for fulfillments. This is used as a corpus for fuzz
+# testing.
+#
+# This script was run using python 3.6
+# When using anaconda python, the following additional packages were installed (using conda install XXX):
+# pyasn1 (conda install pyasn1)
+# cryptography (conda install cryptography)
+# nacl (conda install -c conda-forge pynacl)
+
+import argparse
+import base64
+import binascii
+import codecs
+
+import collections
+from collections import defaultdict
+
+import cryptoconditions_asn1
+
+import cryptography
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.hazmat.primitives import serialization
+
+import itertools
+
+import known_signing_keys
+
+import nacl
+import nacl.encoding
+import nacl.hash
+import nacl.signing
+
+
+import json_test_cases
+import logging
+from pathlib import Path
+
+import pyasn1
+from pyasn1.type import univ
+from pyasn1.codec.der.decoder import decode
+from pyasn1.codec.der.encoder import encode
+
+import os
+import string
+
+from IPython.core.debugger import Tracer
+
+logging.basicConfig(filename='conditions.log', level=logging.DEBUG)
+condition_logger = logging.getLogger('condition')
+
+condition_test_template_prefix = \
+'''
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+// THIS FILE WAS AUTOMATICALLY GENERATED -- DO NOT EDIT
+
+//==============================================================================
+
+#include
+
+namespace ripple {{
+namespace cryptoconditions {{
+
+class Conditions_{RootTestName}_test : public ConditionsTestBase
+{{
+'''
+
+
+condition_test_template_suffix = \
+'''
+}};
+
+BEAST_DEFINE_TESTSUITE(Conditions_{RootTestName}, conditions, ripple);
+}} // cryptoconditions
+}} // ripple
+'''
+
+
+def to_bytes(v):
+ if type(v) == bytes:
+ return v
+ return v.encode()
+
+
+def to_string(v):
+ if type(v) == str:
+ return v
+ return v.decode('utf8')
+
+
+def case_and_type_insensitive_cmp(s1, s2):
+ return to_bytes(s1).lower() == to_bytes(s2).lower()
+
+
+def urlsafe_base64_to_hex(data):
+ '''The sample cryptocondition json base 64 encoding is not correctly padded.
+ This function adds the correct padding before decoding
+ '''
+ padding = len(data) % 4
+ if padding != 0:
+ data += b'=' * (4 - padding)
+ return binascii.hexlify(base64.urlsafe_b64decode(data))
+
+
+def set_with_tags(parent, name, value):
+ c = parent.componentType
+ idx = c.getPositionByName(name)
+ proto = c[idx][1]
+ t = type(value)
+ if t in [
+ pyasn1.type.univ.OctetString, pyasn1.type.univ.Integer,
+ pyasn1.type.univ.BitString
+ ]:
+ value_with_tags = value.clone(
+ tagSet=proto.getEffectiveTagSet(),
+ subtypeSpec=proto.getSubtypeSpec())
+ else:
+ value_with_tags = value.clone(
+ tagSet=proto.getEffectiveTagSet(),
+ subtypeSpec=proto.getSubtypeSpec(),
+ cloneValueFlag=True)
+ parent[name] = value_with_tags
+
+
+def encode_it(a):
+ return to_string(binascii.hexlify(encode(a)))
+
+
+def escape_hex(v):
+ v = to_string(v)
+ if len(v) % 2:
+ raise ValueError('String must have an even number of characters')
+ r = ''
+ for i in range(0, len(v), 2):
+ r += '\\x' + v[i] + v[i + 1]
+ return r
+
+
+def array_data_hex(v):
+ v = to_string(v)
+ if len(v) % 2:
+ raise ValueError('String must have an even number of characters')
+ r = []
+ for i in range(0, len(v), 2):
+ r.append('0x' + v[i] + v[i + 1])
+ return ', '.join(r)
+
+
+def write_array(f, var_name, str_data):
+ if len(str_data) % 2:
+ raise ValueError('String must have an even number of characters')
+ f.write('std::array const {v_name}{{{{{data}}}}};\n'.
+ format(
+ v_name=var_name,
+ len=len(str_data) // 2,
+ data=array_data_hex(str_data)))
+
+
+def write_cpp_string_literal(f, s, col_width=60):
+ if col_width % 3:
+ raise ValueError(
+ 'Col width must be a multiple of 3 to handle hex data')
+ cur_col = 0
+ if not len(s):
+ f.write('""')
+ while cur_col < len(s):
+ f.write('"{}"'.format(s[cur_col:cur_col + col_width]))
+ cur_col += col_width
+ f.write('s;\n')
+
+
+def bitset_to_tuple(seq):
+ if seq:
+ result = [0] * (1 + max(seq))
+ else:
+ result = [0]
+ for b in seq:
+ if b >= 32 or b < 0:
+ raise ValueError('Can only set bits between 0 and 31')
+ result[b] = 1
+ return tuple(result)
+
+
+def bitset_to_int(seq):
+ result = 0
+ for b in seq:
+ if b >= 32 or b < 0:
+ raise ValueError('Can only set bits between 0 and 31')
+ result |= (1 << b)
+ return result
+
+
+preimageSha256TypeId = 0
+prefixSha256TypeId = 1
+thresholdSha256TypeId = 2
+rsaSha256TypeId = 3
+ed25519Sha256TypeId = 4
+
+
+def asn1_type(id):
+ d = {
+ preimageSha256TypeId: 'preimageSha256',
+ prefixSha256TypeId: 'prefixSha256',
+ thresholdSha256TypeId: 'thresholdSha256',
+ rsaSha256TypeId: 'rsaSha256',
+ ed25519Sha256TypeId: 'ed25519Sha256'
+ }
+ return d[id]
+
+
+def cpp_type(id):
+ return 'Type::' + asn1_type(id)
+
+
+def short_type(id):
+ d = {
+ preimageSha256TypeId: 'preim',
+ prefixSha256TypeId: 'prefix',
+ thresholdSha256TypeId: 'thresh',
+ rsaSha256TypeId: 'rsa',
+ ed25519Sha256TypeId: 'ed'
+ }
+ return d[id]
+
+
+def json_type_string_to_type_id(id):
+ d = {
+ 'preimage-sha-256': preimageSha256TypeId,
+ 'prefix-sha-256': prefixSha256TypeId,
+ 'threshold-sha-256': thresholdSha256TypeId,
+ 'rsa-sha-256': rsaSha256TypeId,
+ 'ed25519-sha-256': ed25519Sha256TypeId
+ }
+ return d[id]
+
+
+class Condition:
+ def __init__(self, fulfillment):
+ self.fulfillment = fulfillment
+
+ def type_id(self):
+ return self.fulfillment.type_id()
+
+ def type_name(self):
+ return asn1_type(self.type_id())
+
+ def cost(self):
+ return self.fulfillment.cost()
+
+ def fingerprint(self):
+ if self.type_id() == preimageSha256TypeId:
+ encoding = self.fulfillment.preimage
+ else:
+ encoding = self.fulfillment.encoded_fingerprint()
+ digest = nacl.hash.sha256(encoding, encoder=nacl.encoding.HexEncoder)
+ return digest
+
+ def to_asn1(self):
+ digest = self.fingerprint()
+
+ c = cryptoconditions_asn1.Condition().componentType
+ idx = c.getPositionByName(self.type_name())
+ asn1_condition = c[idx][1].clone()
+ set_with_tags(
+ asn1_condition,
+ 'fingerprint',
+ univ.OctetString(hexValue=to_string(digest)))
+ set_with_tags(asn1_condition, 'cost', univ.Integer(self.cost()))
+
+ subtypes_set = self.fulfillment.subtype_ids()
+ if subtypes_set:
+ subtypes = bitset_to_tuple(subtypes_set)
+ set_with_tags(asn1_condition, 'subtypes', univ.BitString(subtypes))
+
+ result = cryptoconditions_asn1.Condition()
+ result[self.type_name()] = asn1_condition
+ return result
+
+ def decl_var_name(self):
+ return '{}{}Cond'.format(
+ short_type(self.type_id()).capitalize(), self.fulfillment.id)
+
+ def write_test_structure(self, f, level=0):
+ f.write(' // {} {}\n'.format('*' * (level + 1), self.decl_var_name()))
+
+ def write_decl(self, f):
+ '''
+ Condition(Type t, std::uint32_t cost, std::array const& fingerprint, std::bitset<5> const& subtypes)
+ '''
+ var_name = self.decl_var_name()
+ t = cpp_type(self.type_id())
+ digest = self.fingerprint()
+ fingerprint_var = '{}ConditionFingerprint'.format(var_name)
+ f.write('std::array const {var_name}={{{{ {array_data} }}}};'.
+ format(var_name=fingerprint_var, array_data=array_data_hex(digest)))
+ cost = str(self.cost())
+ subtypes_as_int = 0
+ for i in self.fulfillment.subtype_ids():
+ subtypes_as_int += 1 << i
+ params = ', '.join(
+ [t, cost, fingerprint_var, 'std::bitset<5>{{{}}}'.format(subtypes_as_int)])
+
+ f.write('Condition const {var_name}{{{params}}};\n'.format(
+ var_name=var_name, params=params))
+
+ def write_test(self, f):
+ var_name = self.decl_var_name()
+ f.write('{\n')
+ f.write('auto const {var_name}EncodedCondition="{encoded}"s;\n'.format(
+ var_name=var_name, encoded=escape_hex(encode_it(self.to_asn1()))))
+
+ f.write(
+ 'auto const {var_name}EncodedFingerprint="{encoded}";\n'.format(
+ var_name=var_name,
+ encoded=escape_hex(
+ binascii.hexlify(self.fulfillment.encoded_fingerprint()))))
+ self.write_decl(f, var_name)
+ f.write('}\n')
+
+
+class Fulfillment:
+ next_id = 0
+
+ def __init__(self):
+ self.msg = b''
+ self.id = Fulfillment.next_id
+ Fulfillment.next_id += 1
+
+ @classmethod
+ def reset_id(cls):
+ cls.next_id = 0
+
+ @classmethod
+ def create_from_json(cls, json_init, json_check=None):
+ prototypes = {
+ 'preimage-sha-256': Preimage,
+ 'prefix-sha-256': Prefix,
+ 'threshold-sha-256': Threshold,
+ 'rsa-sha-256': RsaSha256,
+ 'ed25519-sha-256': Ed25519
+ }
+ return prototypes[json_init['type']](json_init=json_init,
+ json_check=json_check)
+
+ def check_json(self, json):
+ if not case_and_type_insensitive_cmp(
+ encode_it(self.to_asn1()), json['fulfillment']):
+ raise ValueError('Expected fulfillment mismatch')
+ if not case_and_type_insensitive_cmp(
+ encode_it(self.condition().to_asn1()),
+ json['conditionBinary']):
+ raise ValueError('Expected condition mismatch')
+ if not case_and_type_insensitive_cmp(
+ binascii.hexlify(self.encoded_fingerprint()),
+ to_bytes(json['fingerprintContents'])):
+ raise ValueError('Fingerprint contents mismatch')
+ if self.cost() != json['cost']:
+ raise ValueError('Cost mismatch')
+ st_from_json = set(
+ [json_type_string_to_type_id(tid) for tid in json['subtypes']])
+ if self.subtype_ids() != st_from_json:
+ raise ValueError('Subtypes mismatch')
+ self.msg = escape_hex(to_bytes(json['message']))
+
+ @classmethod
+ def create_from_pylist(cls, pylist):
+ prototypes = {
+ 'preim': Preimage,
+ 'prefix': Prefix,
+ 'thresh': Threshold,
+ 'rsa': RsaSha256,
+ 'ed': Ed25519
+ }
+ t = type(pylist)
+ if t == str:
+ return prototypes[pylist](pylist=pylist)
+ if t != list:
+ raise ValueError('Bad pylist')
+ if len(pylist) == 0:
+ return []
+ if type(pylist[0]) == str:
+ if pylist[0] == 'thresh':
+ return Threshold(pylist=pylist)
+ elif pylist[0] == 'prefix':
+ return Prefix(pylist=pylist)
+ return [Fulfillment.create_from_pylist(f) for f in pylist]
+
+ def set_msg_self_and_children(self, msg):
+ '''
+ Set the message of a fulfillment so it succeeds.
+ This will set the message of child fulfillments so they
+ will succeeds as well, in particular, this means prefix subcondition
+ will change their prefixes and set its message so
+ prefix+new_message==set_msg
+ '''
+ self.set_msg(msg)
+
+ def self_and_subtype_ids(self):
+ return set([self.type_id()])
+
+ def subtype_ids(self):
+ return set()
+
+ def condition(self):
+ return Condition(self)
+
+ def decl_var_name(self):
+ return '{}{}'.format(short_type(self.type_id()), self.id)
+
+ def depth_first_fulfillments(self, result):
+ result.append(self)
+
+ def write_test_structure(self, f, level=0):
+ if level == 0:
+ f.write(' // Fulfillment structure\n')
+ f.write(' // {} {}\n'.format('*' * (level + 1), self.decl_var_name()))
+
+ def write_test(self, f):
+ self.write_test_structure(f)
+ f.write('\n\n')
+ allfulfillments = []
+ self.depth_first_fulfillments(allfulfillments)
+ for s in allfulfillments:
+ s.write_init_data(f)
+ f.write('\n\n')
+ for s in allfulfillments[:-1]:
+ s.write_decl(f, is_unique_ptr=True)
+ allfulfillments[-1].write_decl(f, is_unique_ptr=True)
+
+ var_name = self.decl_var_name()
+ f.write('{\n')
+ f.write('auto {var_name}EncodedFulfillment='.format(
+ var_name=var_name))
+ write_cpp_string_literal(f, escape_hex(encode_it(self.to_asn1())))
+ f.write('auto const {var_name}EncodedCondition='.format(
+ var_name=var_name))
+ write_cpp_string_literal(
+ f, escape_hex(encode_it(self.condition().to_asn1())))
+ f.write('auto const {var_name}EncodedFingerprint='.format(
+ var_name=var_name))
+ write_cpp_string_literal(
+ f, escape_hex(binascii.hexlify(self.encoded_fingerprint())))
+ f.write('check(')
+ f.write('''
+ std::move({var_name}),
+ {var_name}Msg,
+ std::move({var_name}EncodedFulfillment),
+ {var_name}EncodedCondition,
+ {var_name}EncodedFingerprint);
+ '''.format(var_name=var_name))
+ f.write('}\n')
+
+
+def load_ed25519_key(signing_key):
+ return nacl.signing.SigningKey(signing_key, nacl.encoding.HexEncoder)
+
+
+class Ed25519(Fulfillment):
+
+ known_keys = [load_ed25519_key(k) for k in known_signing_keys.ed25519_known_keys_serialized]
+
+ def __init__(self,
+ msg=b'Attack at Dawn',
+ signing_key=None,
+ json_init=None,
+ json_check=None,
+ pylist=None):
+ super().__init__()
+ self.cached_verify_key_hex = None
+ self.cached_signature_hex = None
+ self.signing_key = None
+ if json_init:
+ self.from_json(json_init)
+ if json_check:
+ self.check_json(json_check)
+ return
+ if pylist is not None and pylist != short_type(self.type_id()):
+ raise ValueError('Ill formed pylist spec')
+ if signing_key is None:
+ signing_key = Ed25519.known_keys[self.id % len(Ed25519.known_keys)]
+
+ self.signing_key = signing_key
+ self.set_msg(msg)
+
+ def from_json(self, json):
+ self.cached_verify_key_hex = to_bytes(
+ urlsafe_base64_to_hex(to_bytes(json['publicKey'])))
+ self.cached_signature_hex = to_bytes(
+ urlsafe_base64_to_hex(to_bytes(json['signature'])))
+
+ def set_msg(self, msg):
+ msg = to_bytes(msg)
+ self.msg = msg
+ self.signature = self.signing_key.sign(msg)[0:-len(msg)]
+
+ def signature_hex(self):
+ if self.cached_signature_hex:
+ return self.cached_signature_hex
+ return binascii.hexlify(self.signature)
+
+ def signing_key_hex(self):
+ return self.signing_key.encode(encoder=nacl.encoding.HexEncoder)
+
+ def verify_key_hex(self):
+ if self.cached_verify_key_hex:
+ return self.cached_verify_key_hex
+ return self.signing_key.verify_key.encode(
+ encoder=nacl.encoding.HexEncoder)
+
+ def cost(self):
+ # see crypto-conditions spec:
+ # https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#page-27
+ return 131072
+
+ def type_id(self):
+ return ed25519Sha256TypeId
+
+ def to_asn1(self):
+ result = cryptoconditions_asn1.Fulfillment()
+ idx = result.componentType.getPositionByName('ed25519Sha256')
+ # tag types must match - creating a choice independent of the tags (i.e. not cloning here)
+ # and then setting f['ed25519Sha256'] = my_fulfillment_without_correct_tags will not work
+ choice_ed_f = result.componentType[idx][1].clone()
+
+ vk_hex = to_string(self.verify_key_hex())
+ sig_hex = to_string(self.signature_hex())
+ set_with_tags(
+ choice_ed_f, 'publicKey', univ.OctetString(hexValue=vk_hex))
+ set_with_tags(
+ choice_ed_f, 'signature', univ.OctetString(hexValue=sig_hex))
+ result['ed25519Sha256'] = choice_ed_f
+ return result
+
+ def encoded_fingerprint(self):
+ fingerprint = cryptoconditions_asn1.Ed25519FingerprintContents()
+ set_with_tags(
+ fingerprint,
+ 'publicKey',
+ univ.OctetString(hexValue=to_string(self.verify_key_hex())))
+ return encode(fingerprint)
+
+ def write_init_data(self, f):
+ var_name = self.decl_var_name()
+ f.write('auto const {}Msg="{}"s;\n'.format(var_name,
+ to_string(self.msg)))
+ write_array(f, '{}PublicKey'.format(var_name), self.verify_key_hex())
+ write_array(f, '{}Sig'.format(var_name), self.signature_hex())
+ if self.signing_key:
+ write_array(f, '{}SigningKey'.format(var_name),
+ self.signing_key_hex())
+ f.write('(void){}SigningKey;\n'.format(var_name))
+
+ def write_decl(self, f, is_unique_ptr=True, inc_sub_test=False):
+ var_name = self.decl_var_name()
+ if not is_unique_ptr:
+ f.write(
+ 'Ed25519 const {var_name}({var_name}PublicKey, {var_name}Sig);\n'.
+ format(var_name=var_name))
+ else:
+ f.write(
+ 'auto {var_name}=std::make_unique({var_name}PublicKey, {var_name}Sig);\n'.
+ format(var_name=var_name))
+
+
+def load_rsa_key(signing_key):
+ return serialization.load_pem_private_key(
+ signing_key, password=None, backend=default_backend())
+
+
+class RsaSha256(Fulfillment):
+ known_keys = [load_rsa_key(k) for k in known_signing_keys.rsa_known_keys_serialized]
+
+ def __init__(self,
+ msg=b'Attack at Dawn',
+ signing_key=None,
+ json_init=None,
+ json_check=None,
+ pylist=None):
+ super().__init__()
+ self.cached_verify_key_hex = None
+ self.cached_signature_hex = None
+ self.signing_key = None
+ if json_init:
+ self.from_json(json_init)
+ if json_check:
+ self.check_json(json_check)
+ return
+ if pylist is not None and pylist != short_type(self.type_id()):
+ raise ValueError('Ill formed pylist spec')
+ if signing_key is None:
+ signing_key = RsaSha256.known_keys[self.id % len(RsaSha256.known_keys)]
+
+ self.signing_key = signing_key
+ self.set_msg(msg)
+
+ def from_json(self, json):
+ self.cached_verify_key_hex = to_bytes(
+ urlsafe_base64_to_hex(to_bytes(json['modulus'])))
+ self.cached_signature_hex = to_bytes(
+ urlsafe_base64_to_hex(to_bytes(json['signature'])))
+
+ def set_msg(self, msg):
+ from cryptography.hazmat.primitives import hashes
+ from cryptography.hazmat.primitives.asymmetric import padding
+ msg = to_bytes(msg)
+ self.msg = msg
+
+ self.signature = self.signing_key.sign(
+ self.msg,
+ padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
+ salt_length=256 // 8),
+ hashes.SHA256())
+ # dummy check. Will raise an exception if it does not verify
+ self.signing_key.public_key().verify(
+ self.signature,
+ self.msg,
+ padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
+ salt_length=256 // 8),
+ hashes.SHA256())
+
+ def signature_hex(self):
+ if self.cached_signature_hex:
+ return self.cached_signature_hex
+ return binascii.hexlify(self.signature)
+
+ def verify_key_hex(self):
+ if self.cached_verify_key_hex:
+ return self.cached_verify_key_hex
+ pk = self.signing_key.public_key()
+ return '{:x}'.format(pk.public_numbers().n)
+
+ def cost(self):
+ if self.cached_signature_hex:
+ key_bytes = len(self.cached_verify_key_hex) // 2
+ else:
+ key_bytes = self.signing_key.key_size // 8
+ return key_bytes * key_bytes
+
+ def type_id(self):
+ return rsaSha256TypeId
+
+ def to_asn1(self):
+ result = cryptoconditions_asn1.Fulfillment()
+ idx = result.componentType.getPositionByName('rsaSha256')
+ # tag types must match - creating a choice independent of the tags (i.e. not cloning here)
+ # and then setting f['rsaSha256'] = my_fulfillment_without_correct_tags will not work
+ choice_rsa_f = result.componentType[idx][1].clone()
+
+ vk_hex = to_string(self.verify_key_hex())
+ sig_hex = to_string(self.signature_hex())
+ set_with_tags(
+ choice_rsa_f, 'modulus', univ.OctetString(hexValue=vk_hex))
+ set_with_tags(
+ choice_rsa_f, 'signature', univ.OctetString(hexValue=sig_hex))
+ result['rsaSha256'] = choice_rsa_f
+ return result
+
+ def encoded_fingerprint(self):
+ fingerprint = cryptoconditions_asn1.RsaFingerprintContents()
+ set_with_tags(
+ fingerprint,
+ 'modulus',
+ univ.OctetString(hexValue=to_string(self.verify_key_hex())))
+ return encode(fingerprint)
+
+ def write_init_data(self, f):
+ var_name = self.decl_var_name()
+ f.write('auto const {}Msg="{}"s;\n'.format(var_name,
+ to_string(self.msg)))
+ write_array(f, '{}PublicKey'.format(var_name), self.verify_key_hex())
+ write_array(f, '{}Sig'.format(var_name), self.signature_hex())
+
+ def write_decl(self, f, is_unique_ptr=True, inc_sub_test=False):
+ var_name = self.decl_var_name()
+ if not is_unique_ptr:
+ f.write('''RsaSha256 const {var_name}(
+ makeSlice({var_name}PublicKey),
+ makeSlice({var_name}Sig));\n'''
+ .format(var_name=var_name))
+ else:
+ f.write('''auto {var_name}=std::make_unique(
+ makeSlice({var_name}PublicKey),
+ makeSlice({var_name}Sig));\n'''
+ .format(var_name=var_name))
+
+
+class Prefix(Fulfillment):
+ def __init__(self,
+ subfulfillment=None,
+ prefix=b'Attack ',
+ msg=b'at Dawn',
+ max_msg_length=None,
+ json_init=None,
+ json_check=None,
+ pylist=None):
+ super().__init__()
+ if json_init:
+ self.from_json(json_init)
+ if json_check:
+ self.check_json(json_check)
+ return
+ if max_msg_length is None:
+ max_msg_length = len(msg)
+ if pylist != None:
+ if len(pylist) < 2 or pylist[0] != short_type(self.type_id()):
+ raise ValueError('Ill formed pylist spec')
+ sub_init = pylist[1] if len(pylist) == 2 else pylist[1:]
+ subfulfillment = Fulfillment.create_from_pylist(sub_init)
+ if subfulfillment is None:
+ raise ValueError('Must specify either json or subfulfillment')
+ self.subfulfillment = subfulfillment
+ self.prefix = to_bytes(prefix)
+ self.msg = to_bytes(msg)
+ self.max_msg_length = max_msg_length
+ self.set_msg(msg)
+
+ def from_json(self, json):
+ self.max_msg_length = json['maxMessageLength']
+ self.prefix = binascii.unhexlify(
+ to_bytes(urlsafe_base64_to_hex(to_bytes(json['prefix']))))
+ self.subfulfillment = Fulfillment.create_from_json(json[
+ 'subfulfillment'])
+
+ def set_msg_self_and_children(self, msg):
+ self.msg = to_bytes(msg)
+ self.max_msg_length = len(msg)
+ self.prefix = to_bytes('P{}'.format(self.id))
+ self.subfulfillment.set_msg_self_and_children(self.prefix + self.msg)
+
+ def set_msg(self, msg):
+ self.msg = to_bytes(msg)
+ self.max_msg_length = len(msg)
+ self.subfulfillment.set_msg(self.prefix + self.msg)
+
+ def cost(self):
+ return len(self.prefix
+ ) + self.max_msg_length + self.subfulfillment.cost() + 1024
+
+ def type_id(self):
+ return prefixSha256TypeId
+
+ def self_and_subtype_ids(self):
+ r = self.subfulfillment.self_and_subtype_ids()
+ r.add(self.type_id())
+ return r
+
+ def subtype_ids(self):
+ r = self.subfulfillment.self_and_subtype_ids()
+ r.discard(self.type_id())
+ return r
+
+ def depth_first_fulfillments(self, result):
+ self.subfulfillment.depth_first_fulfillments(result)
+ result.append(self)
+
+ def write_test_structure(self, f, level=0):
+ super().write_test_structure(f, level)
+ self.subfulfillment.write_test_structure(f, level + 1)
+
+ def to_asn1(self):
+ result = cryptoconditions_asn1.Fulfillment()
+ idx = result.componentType.getPositionByName('prefixSha256')
+ # tag types must match - creating a choice independent of the tags (i.e. not cloning here)
+ # and then setting f['ed25519Sha256'] = my_fulfillment_without_correct_tags will not work
+ choice_f = result.componentType[idx][1].clone()
+
+ set_with_tags(choice_f, 'prefix', univ.OctetString(self.prefix))
+ set_with_tags(choice_f, 'maxMessageLength',
+ univ.Integer(self.max_msg_length))
+
+ set_with_tags(choice_f, 'subfulfillment',
+ self.subfulfillment.to_asn1())
+
+ result['prefixSha256'] = choice_f
+ return result
+
+ def encoded_fingerprint(self):
+ fingerprint = cryptoconditions_asn1.PrefixFingerprintContents()
+ set_with_tags(fingerprint, 'prefix', univ.OctetString(self.prefix))
+ set_with_tags(fingerprint, 'maxMessageLength',
+ univ.Integer(self.max_msg_length))
+ set_with_tags(fingerprint, 'subcondition',
+ self.subfulfillment.condition().to_asn1())
+ return encode(fingerprint)
+
+ def write_init_data(self, f):
+ var_name = self.decl_var_name()
+ f.write('auto const {}Prefix="{}"s;\n'.format(var_name,
+ to_string(self.prefix)))
+ f.write('auto const {}Msg="{}"s;\n'.format(var_name,
+ to_string(self.msg)))
+ f.write('auto const {}MaxMsgLength={};\n'.format(var_name,
+ self.max_msg_length))
+
+ def write_decl(self, f, is_unique_ptr=True, inc_sub_test=False):
+ var_name = self.decl_var_name()
+ if not is_unique_ptr:
+ decl = '''PrefixSha256 const {var_name}(makeSlice({var_name}Prefix),
+ {var_name}MaxMsgLength,
+ std::move({sub_var}));\n'''
+ else:
+ decl = '''auto {var_name} = std::make_unique(makeSlice({var_name}Prefix),
+ {var_name}MaxMsgLength,
+ std::move({sub_var}));\n'''
+ f.write(
+ decl.format(
+ var_name=var_name, sub_var=self.subfulfillment.decl_var_name(
+ )))
+
+
+class Preimage(Fulfillment):
+ def __init__(self,
+ preimage=b'I am root',
+ msg=b'Attack at Dawn',
+ json_init=None,
+ json_check=None,
+ pylist=None):
+ super().__init__()
+ if json_init:
+ self.from_json(json_init)
+ if json_check:
+ self.check_json(json_check)
+ return
+ if pylist is not None and pylist != short_type(self.type_id()):
+ raise ValueError('Ill formed pylist spec')
+ self.preimage = to_bytes(preimage)
+ self.msg = to_bytes(msg)
+ self.set_msg(msg)
+
+ def from_json(self, json):
+ self.preimage = binascii.unhexlify(
+ to_bytes(urlsafe_base64_to_hex(to_bytes(json['preimage']))))
+
+ def set_msg(self, msg):
+ self.msg = to_bytes(msg)
+
+ def cost(self):
+ return len(self.preimage)
+
+ def type_id(self):
+ return preimageSha256TypeId
+
+ def to_asn1(self):
+ result = cryptoconditions_asn1.Fulfillment()
+ idx = result.componentType.getPositionByName('preimageSha256')
+ # tag types must match - creating a choice independent of the tags (i.e. not cloning here)
+ # and then setting f['ed25519Sha256'] = my_fulfillment_without_correct_tags will not work
+ choice_f = result.componentType[idx][1].clone()
+ set_with_tags(choice_f, 'preimage', univ.OctetString(self.preimage))
+ result['preimageSha256'] = choice_f
+ return result
+
+ def encoded_fingerprint(self):
+ # Unlike other cc (that use der encoding), the fingerprint is a hash of the preimage
+ return self.preimage
+
+ def write_init_data(self, f):
+ var_name = self.decl_var_name()
+ f.write('auto const {}Preimage="{}"s;\n'.format(
+ var_name, to_string(self.preimage)))
+ f.write('auto const {}Msg="{}"s;\n'.format(var_name,
+ to_string(self.msg)))
+
+ def write_decl(self, f, is_unique_ptr=True, inc_sub_test=False):
+ var_name = self.decl_var_name()
+ if not is_unique_ptr:
+ decl = '''PreimageSha256 const {var_name}(makeSlice({var_name}Preimage));\n'''
+ else:
+ decl = '''auto {var_name} = std::make_unique(makeSlice({var_name}Preimage));\n'''
+ f.write(decl.format(var_name=var_name))
+
+
+class Threshold(Fulfillment):
+ def __init__(self,
+ subfulfillments=None,
+ subconditions=None,
+ msg=b'Attack at Dawn',
+ json_init=None,
+ json_check=None,
+ pylist=None):
+ super().__init__()
+ if json_init:
+ self.from_json(json_init)
+ if json_check:
+ self.check_json(json_check)
+ return
+ self.subfulfillments = subfulfillments
+ self.subconditions = subconditions
+ if pylist != None:
+ if len(pylist) != 3 or pylist[0] != short_type(self.type_id()):
+ raise ValueError('Ill formed pylist spec')
+ self.subfulfillments = Fulfillment.create_from_pylist(pylist[1])
+ self.subconditions = [f.condition() for f in Fulfillment.create_from_pylist(pylist[2])]
+ self.msg = to_bytes(msg)
+ self.set_msg(msg)
+
+ def from_json(self, json):
+ t = json['threshold']
+ if t != len(json['subfulfillments']):
+ if 'conditionIndexes' not in json:
+ raise ValueError(
+ 'Must specify conditionIndexes to show which fulfillments are actually used.'
+ )
+ else:
+ condIndexes = json['conditionIndexes']
+ else:
+ condIndexes = []
+ self.subfulfillments = [
+ Fulfillment.create_from_json(sf)
+ for i, sf in enumerate(json['subfulfillments'])
+ if i not in condIndexes
+ ]
+ self.subconditions = [
+ Fulfillment.create_from_json(sf).condition()
+ for i, sf in enumerate(json['subfulfillments']) if i in condIndexes
+ ]
+
+ def set_msg(self, msg):
+ self.msg = to_bytes(msg)
+ for f in self.subfulfillments:
+ f.set_msg(msg)
+
+ def set_msg_self_and_children(self, msg):
+ self.msg = to_bytes(msg)
+ for f in self.subfulfillments:
+ f.set_msg_self_and_children(msg)
+
+ def cost(self):
+ costs = [c.cost() for c in self.subconditions
+ ] + [f.cost() for f in self.subfulfillments]
+ costs.sort()
+ n_highests = costs[-len(self.subfulfillments):]
+ return 1024 * (len(self.subfulfillments) + len(self.subconditions)
+ ) + sum(n_highests)
+
+ def type_id(self):
+ return thresholdSha256TypeId
+
+ def self_and_subtype_ids(self):
+ r = set()
+ for s in self.subfulfillments:
+ r |= s.self_and_subtype_ids()
+ for s in self.subconditions:
+ r |= s.fulfillment.self_and_subtype_ids()
+ r.add(self.type_id())
+ return r
+
+ def subtype_ids(self):
+ r = self.self_and_subtype_ids()
+ r.discard(self.type_id())
+ return r
+
+ def depth_first_fulfillments(self, result):
+ for f in self.subfulfillments:
+ f.depth_first_fulfillments(result)
+ result.append(self)
+
+ def write_test_structure(self, f, level=0):
+ super().write_test_structure(f, level)
+ for c in self.subconditions:
+ c.write_test_structure(f, level + 1)
+ for ful in self.subfulfillments:
+ ful.write_test_structure(f, level + 1)
+
+ def to_asn1(self):
+ result = cryptoconditions_asn1.Fulfillment()
+ idx = result.componentType.getPositionByName('thresholdSha256')
+ # tag types must match - creating a choice independent of the tags (i.e. not cloning here)
+ # and then setting f['ed25519Sha256'] = my_fulfillment_without_correct_tags will not work
+ choice_f = result.componentType[idx][1].clone()
+
+ a_f = univ.SetOf(componentType=cryptoconditions_asn1.Fulfillment())
+ for s in self.subfulfillments:
+ a_f.append(s.to_asn1())
+ set_with_tags(choice_f, 'subfulfillments', a_f)
+ a_c = univ.SetOf(componentType=cryptoconditions_asn1.Condition())
+ for s in self.subconditions:
+ a_c.append(s.to_asn1())
+ set_with_tags(choice_f, 'subconditions', a_c)
+
+ result['thresholdSha256'] = choice_f
+ return result
+
+ def encoded_fingerprint(self):
+ fingerprint = cryptoconditions_asn1.ThresholdFingerprintContents()
+ set_with_tags(fingerprint, 'threshold',
+ univ.Integer(len(self.subfulfillments)))
+ a_c = univ.SetOf(componentType=cryptoconditions_asn1.Condition())
+ for s in self.subfulfillments:
+ a_c.append(s.condition().to_asn1())
+ for s in self.subconditions:
+ a_c.append(s.to_asn1())
+ set_with_tags(fingerprint, 'subconditions', a_c)
+ return encode(fingerprint)
+
+ def subfulfill_var(self):
+ return '{}Subfulfillments'.format(self.decl_var_name())
+
+ def subcond_var(self):
+ return '{}Subconditions'.format(self.decl_var_name())
+
+ def write_init_data(self, f):
+ var_name = self.decl_var_name()
+ f.write('auto const {}Msg="{}"s;\n'.format(var_name,
+ to_string(self.msg)))
+ for sc in self.subconditions:
+ sc.write_decl(f)
+
+ def write_decl(self, f, is_unique_ptr=True, inc_sub_test=False):
+ var_name = self.decl_var_name()
+ f.write('std::vector> {var_name};'.format(
+ var_name=self.subfulfill_var()))
+ for sf in self.subfulfillments:
+ f.write('{var_name}.emplace_back(std::move({sf_var}));'.format(
+ var_name=self.subfulfill_var(), sf_var=sf.decl_var_name()))
+
+ subconditions_init = ', '.join(
+ ['{}'.format(sc.decl_var_name()) for sc in self.subconditions])
+ if subconditions_init:
+ subconditions_init = '{' + subconditions_init + '}'
+ subcond_var = '{}Subconditions'.format(var_name)
+ f.write('std::vector {var_name}{{{subconditions_init}}};'.
+ format(
+ var_name=self.subcond_var(),
+ subconditions_init=subconditions_init))
+ if not is_unique_ptr:
+ decl = '''ThresholdSha256 const {var_name}(std::move({subfulfil_var}),
+ std::move({subcond_var}));\n'''
+ else:
+ decl = '''auto {var_name} = std::make_unique(std::move({subfulfil_var}),
+ std::move({subcond_var}));\n'''
+ f.write(
+ decl.format(
+ var_name=var_name,
+ subfulfil_var=self.subfulfill_var(),
+ subcond_var=self.subcond_var()))
+
+
+class TestWriter:
+ def __init__(self, result_file):
+ self.result_file = result_file
+ # function names to tests to run
+ self.test_names = []
+ self.test_ids = defaultdict(lambda: 0)
+
+ def checkout_test_name(self, fulfillment):
+ n = short_type(fulfillment.type_id())
+ id = self.test_ids[n]
+ self.test_ids[n] += 1
+ return '{}{}'.format(n.capitalize(), id)
+
+ def write_test(self, fulfillment):
+ test_name = self.checkout_test_name(fulfillment)
+ full_test_name = 'test' + test_name
+ self.result_file.write('''
+ void
+ {full_test_name}(){{
+ testcase("{test_name}");
+
+ using namespace std::string_literals;
+ using namespace ripple::cryptoconditions;
+
+ '''.format(
+ full_test_name=full_test_name, test_name=test_name))
+ self.test_names.append(full_test_name)
+ fulfillment.write_test(self.result_file)
+ Fulfillment.reset_id()
+ self.result_file.write('}\n')
+
+ def write_test_case_ed(self):
+ ed = Ed25519()
+ self.write_test(ed)
+
+ def write_test_case_rsa(self):
+ rsa = RsaSha256()
+ self.write_test(rsa)
+
+ def write_test_case_preimage(self):
+ p = Preimage()
+ self.write_test(p)
+
+ def write_test_case_prefix(self):
+ ed = Ed25519()
+ p = Prefix(ed)
+ self.write_test(p)
+
+ def write_test_case_threshold(self):
+ prefix = b'Attack '
+ msg = b'at Dawn'
+ p = Prefix(Ed25519(msg=prefix + msg), msg=msg)
+ ed = Ed25519(msg=msg)
+ rsa = RsaSha256(msg=msg)
+ subfulfillments = [p, ed, rsa]
+ subconditions = []
+ t = Threshold(subfulfillments, subconditions)
+ self.write_test(t)
+
+ def write_test_case(self, test_type):
+ if test_type == ed25519Sha256TypeId:
+ self.write_test_case_ed()
+ elif test_type == prefixSha256TypeId:
+ self.write_test_case_prefix()
+ elif test_type == rsaSha256TypeId:
+ self.write_test_case_rsa()
+ elif test_type == thresholdSha256TypeId:
+ self.write_test_case_threshold()
+ elif test_type == preimageSha256TypeId:
+ self.write_test_case_preimage()
+
+ def save_pylist_test_case(self, pylist):
+ f = Fulfillment.create_from_pylist(pylist)
+ f.set_msg_self_and_children(string.ascii_lowercase)
+ self.write_test(f)
+
+ def save_json_test_case(self, tc):
+ test_type = tc['json']['type']
+ f = Fulfillment.create_from_json(json_init=tc['json'], json_check=tc)
+ self.write_test(f)
+
+ def write_run(self):
+ self.result_file.write('''
+ void
+ run(){''')
+ for t in self.test_names:
+ self.result_file.write('{}();\n'.format(t))
+ self.result_file.write('}\n')
+
+
+def test_case_str(test_type=prefixSha256TypeId):
+ import io
+ f = io.StringIO()
+ tw = TestWriter(f)
+ tw.write_test_case(test_type)
+ return f.getvalue()
+
+
+def save_test_case(file_name, test_type=prefixSha256TypeId):
+ with open(file_name, 'w') as f:
+ tw = TestWriter(f)
+ tw.write_test_case(test_type)
+
+
+def save_json_test_cases(test_writer):
+ for tc in json_test_cases.test_cases:
+ test_writer.save_json_test_case(tc)
+ test_type = tc['json']['type']
+
+
+def partitioned_test_cases():
+ '''return a dictionary of list of test cases. The key will be the top level
+ cryptocondition type. The modivation for partitioning the tests is otherwise
+ the single file is very large and hard for editors to deal with.
+ '''
+ result = defaultdict(list)
+ keys = ['thresh', 'prefix', 'preim', 'rsa', 'ed']
+ def add_to_result(l):
+ k = l[0] if isinstance(l, list) else l
+ if k not in keys:
+ raise ValueError('Unknown test type: {}'.format(k))
+ result[k].append(l)
+ for tc in ['preim', 'rsa', 'ed']:
+ add_to_result(tc)
+ pre0 = ['prefix', tc]
+ add_to_result(pre0)
+ thresh0 = ['thresh', [tc], []]
+ thresh1 = ['thresh', [tc], ['preim', 'rsa', 'ed']]
+ thresh2 = ['thresh', [tc, thresh1], ['preim', 'rsa', 'ed']]
+ thresh3 = ['thresh', [tc, thresh1], ['preim', 'rsa', 'ed', thresh1]]
+ all_thresh = [thresh0, thresh1, thresh2, thresh3]
+ for i in all_thresh:
+ add_to_result(i)
+ prepre0 = ['prefix', 'prefix', tc]
+ prepre1 = ['prefix', 'prefix', pre0]
+ prepre2 = ['prefix', 'prefix', thresh0]
+ prepre3 = ['prefix', 'prefix', thresh1]
+ prepre4 = ['prefix', 'prefix', thresh2]
+ prepre5 = ['prefix', 'prefix', thresh3]
+ all_prepre = [prepre0, prepre1, prepre2, prepre3, prepre4, prepre5]
+ for i in all_prepre:
+ add_to_result(i)
+ for a,b,c in zip(all_prepre + all_thresh, itertools.cycle(all_prepre), itertools.cycle(all_thresh)):
+ add_to_result(['thresh', [a, b, c], ['preim', 'rsa', 'ed']])
+ add_to_result(['thresh', [a, 'preim', 'rsa', 'ed'], ['preim', 'rsa', 'ed', b, c]])
+ return result
+
+
+def save_all_test_cases(file_name_prefix, inc_json=True):
+ test_cases = partitioned_test_cases()
+ for root_condition_name, test_list in test_cases.items():
+ file_name = file_name_prefix+root_condition_name+'.cpp'
+ with open(file_name, 'w') as f:
+ f.write(condition_test_template_prefix.format(RootTestName=root_condition_name))
+ tw = TestWriter(f)
+ for tc in test_list:
+ tw.save_pylist_test_case(tc)
+ tw.write_run()
+ f.write(condition_test_template_suffix.format(RootTestName=root_condition_name))
+
+ if inc_json:
+ root_condition_name = 'json'
+ file_name = file_name_prefix+root_condition_name+'.cpp'
+ with open(file_name, 'w') as f:
+ f.write(condition_test_template_prefix.format(RootTestName=root_condition_name))
+ tw = TestWriter(f)
+ save_json_test_cases(tw)
+ tw.write_run()
+ f.write(condition_test_template_suffix.format(RootTestName=root_condition_name))
+
+
+def save_fuzz_corpus(corpus_dir_path):
+ cdp = Path(corpus_dir_path)
+ fulfillments_dir = cdp / 'fulfillments'
+ conditions_dir = cdp / 'conditions'
+ if not fulfillments_dir.is_dir():
+ os.makedirs(fulfillments_dir)
+ if not conditions_dir.is_dir():
+ os.makedirs(conditions_dir)
+
+ test_cases = partitioned_test_cases()
+
+ fulfillments = []
+ for root_condition_name, test_list in test_cases.items():
+ for tc in test_list:
+ fulfillments.append(Fulfillment.create_from_pylist(tc))
+
+ for tc in json_test_cases.test_cases:
+ fulfillments.append(Fulfillment.create_from_json(json_init=tc['json'], json_check=tc))
+
+ for i,f in enumerate(fulfillments):
+ file_name = '{}_{}.bin'.format(short_type(f.type_id()), i)
+ with open(fulfillments_dir / file_name, 'wb') as out:
+ out.write(encode(f.to_asn1()))
+ with open(conditions_dir / file_name, 'wb') as out:
+ out.write(encode(f.condition().to_asn1()))
+
+def parse_args():
+ parser = argparse.ArgumentParser(
+ description=('Generate test files for cryptocondtions'))
+ parser.add_argument(
+ '--fuzz',
+ '-f',
+ help=('fuzz corpus directory'), )
+ parser.add_argument(
+ '--prefix',
+ '-p',
+ help=('c++ test cases file name prefix'), )
+ return parser.parse_args()
+
+def run_main():
+ args = parse_args()
+ if not args.fuzz and not args.prefix:
+ print('Must specify at least one of --fuzz or --prefix')
+ return
+
+ if args.fuzz:
+ save_fuzz_corpus(args.fuzz)
+ if args.prefix:
+ save_all_test_cases(args.prefix)
+
+if __name__ == '__main__':
+ run_main()
diff --git a/bin/crypto_conditions_test_gen/cryptoconditions_asn1.py b/bin/crypto_conditions_test_gen/cryptoconditions_asn1.py
new file mode 100644
index 00000000000..714e2020bdd
--- /dev/null
+++ b/bin/crypto_conditions_test_gen/cryptoconditions_asn1.py
@@ -0,0 +1,240 @@
+# Auto-generated by asn1ate v.0.5.1.dev from appendix_c.asn1
+# (last modified on 2017-04-24 16:44:58.756111)
+
+from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful
+
+
+class ConditionTypes(univ.BitString):
+ pass
+
+
+ConditionTypes.namedValues = namedval.NamedValues(
+ ('preImageSha256', 0), ('prefixSha256', 1), ('thresholdSha256', 2),
+ ('rsaSha256', 3), ('ed25519Sha256', 4))
+
+
+class CompoundSha256Condition(univ.Sequence):
+ pass
+
+
+CompoundSha256Condition.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'fingerprint',
+ univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(
+ 32, 32)).subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'cost',
+ univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(
+ 0, 4294967295)).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))),
+ namedtype.NamedType(
+ 'subtypes',
+ ConditionTypes().subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 2))))
+
+
+class SimpleSha256Condition(univ.Sequence):
+ pass
+
+
+SimpleSha256Condition.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'fingerprint',
+ univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(
+ 32, 32)).subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'cost',
+ univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(
+ 0, 4294967295)).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))))
+
+
+class Condition(univ.Choice):
+ pass
+
+
+Condition.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'preimageSha256',
+ SimpleSha256Condition().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 0))),
+ namedtype.NamedType(
+ 'prefixSha256',
+ CompoundSha256Condition().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 1))),
+ namedtype.NamedType(
+ 'thresholdSha256',
+ CompoundSha256Condition().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 2))),
+ namedtype.NamedType(
+ 'rsaSha256',
+ SimpleSha256Condition().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 3))),
+ namedtype.NamedType(
+ 'ed25519Sha256',
+ SimpleSha256Condition().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 4))))
+
+
+class Ed25519FingerprintContents(univ.Sequence):
+ pass
+
+
+Ed25519FingerprintContents.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'publicKey',
+ univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(
+ 32, 32)).subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 0))))
+
+
+class Ed25519Sha512Fulfillment(univ.Sequence):
+ pass
+
+
+Ed25519Sha512Fulfillment.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'publicKey',
+ univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(
+ 32, 32)).subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'signature',
+ univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(
+ 64, 64)).subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 1))))
+
+
+class RsaSha256Fulfillment(univ.Sequence):
+ pass
+
+
+RsaSha256Fulfillment.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'modulus',
+ univ.OctetString().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'signature',
+ univ.OctetString().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))))
+
+
+class PreimageFulfillment(univ.Sequence):
+ pass
+
+
+PreimageFulfillment.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'preimage',
+ univ.OctetString().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 0))))
+
+
+class PrefixFulfillment(univ.Sequence):
+ pass
+
+
+class ThresholdFulfillment(univ.Sequence):
+ pass
+
+
+class Fulfillment(univ.Choice):
+ pass
+
+
+PrefixFulfillment.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'prefix',
+ univ.OctetString().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'maxMessageLength',
+ univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(
+ 0, 4294967295)).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))),
+ namedtype.NamedType(
+ 'subfulfillment',
+ Fulfillment().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 2))))
+
+ThresholdFulfillment.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'subfulfillments',
+ univ.SetOf(componentType=Fulfillment()).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'subconditions',
+ univ.SetOf(componentType=Condition()).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))))
+
+Fulfillment.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'preimageSha256',
+ PreimageFulfillment().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 0))),
+ namedtype.NamedType(
+ 'prefixSha256',
+ PrefixFulfillment().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 1))),
+ namedtype.NamedType(
+ 'thresholdSha256',
+ ThresholdFulfillment().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 2))),
+ namedtype.NamedType(
+ 'rsaSha256',
+ RsaSha256Fulfillment().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 3))),
+ namedtype.NamedType(
+ 'ed25519Sha256',
+ Ed25519Sha512Fulfillment().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatConstructed, 4))))
+
+
+class PrefixFingerprintContents(univ.Sequence):
+ pass
+
+
+PrefixFingerprintContents.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'prefix',
+ univ.OctetString().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'maxMessageLength',
+ univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(
+ 0, 4294967295)).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))),
+ namedtype.NamedType(
+ 'subcondition',
+ Condition().subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatConstructed, 2))))
+
+
+class RsaFingerprintContents(univ.Sequence):
+ pass
+
+
+RsaFingerprintContents.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'modulus',
+ univ.OctetString().subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 0))))
+
+
+class ThresholdFingerprintContents(univ.Sequence):
+ pass
+
+
+ThresholdFingerprintContents.componentType = namedtype.NamedTypes(
+ namedtype.NamedType(
+ 'threshold',
+ univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(
+ 1, 65535)).subtype(implicitTag=tag.Tag(tag.tagClassContext,
+ tag.tagFormatSimple, 0))),
+ namedtype.NamedType(
+ 'subconditions',
+ univ.SetOf(componentType=Condition()).subtype(implicitTag=tag.Tag(
+ tag.tagClassContext, tag.tagFormatSimple, 1))))
diff --git a/bin/crypto_conditions_test_gen/decode_der.py b/bin/crypto_conditions_test_gen/decode_der.py
new file mode 100644
index 00000000000..49f7977e440
--- /dev/null
+++ b/bin/crypto_conditions_test_gen/decode_der.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+import binascii
+import codecs
+import pyasn1
+import numpy as np
+from pyasn1.type import univ
+from pyasn1.codec.der import encoder
+
+
+def to_array(hex_string):
+ b = codecs.decode(hex_string.replace(' ',''), 'hex')
+ return np.frombuffer(b,dtype=np.int8)
+
+
+def to_bitstring(x):
+ b = f'{x:b}'
+ t = tuple(reversed([int(i) for i in b]))
+ return univ.BitString(t)
+
+
+def to_string(v):
+ if type(v) == str:
+ return v
+ return v.decode('utf8')
+
+
+def escape_hex(v):
+ v = to_string(v)
+ if len(v) % 2:
+ raise ValueError('String must have an even number of characters')
+ r = ''
+ for i in range(0, len(v), 2):
+ r += '\\x' + v[i] + v[i + 1]
+ return r
+
+
+def encode_bitstring(x):
+ return escape_hex(binascii.hexlify(encoder.encode(to_bitstring(x))))
+
+
+def bitstring_testcases(start, end, lshift=0, offset=0):
+ mul = 2**lshift
+ cases = [((i*mul+offset), encode_bitstring(i*mul+offset)) for i in range(start,end)]
+ return cases
+
+
+def write_bitstring_testcases(f, start, end, lshift=0, offset=0):
+ ts = bitstring_testcases(start, end, lshift, offset)
+
+ f.write('''
+ {
+ std::array, 32>
+ testCases = {{
+ ''')
+ to_write = [f'std::make_pair({bits}ull, "{encoding}"s)' for bits, encoding in ts]
+ f.write(', '.join(to_write))
+ f.write('}};')
+ f.write('''
+ doTest(testCases, std::integral_constant{});
+ doTest(testCases, std::integral_constant{});
+ }
+ ''')
+
+
+def save_all_test_cases(file_name):
+ with open(file_name, 'w') as f:
+ write_bitstring_testcases(f, 0, 32)
+ write_bitstring_testcases(f, 0, 32, lshift = 14)
diff --git a/bin/crypto_conditions_test_gen/generate_known_keys.py b/bin/crypto_conditions_test_gen/generate_known_keys.py
new file mode 100644
index 00000000000..8a6121d739d
--- /dev/null
+++ b/bin/crypto_conditions_test_gen/generate_known_keys.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+import base64
+import binascii
+import codecs
+
+import collections
+from collections import defaultdict
+
+import cryptoconditions_asn1
+
+import cryptography
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.hazmat.primitives import serialization
+
+import nacl
+import nacl.encoding
+import nacl.hash
+import nacl.signing
+
+
+def generate_rsa_keys(n):
+ '''
+ return a list of serializations of n rsa private keys
+ This list will be used for known keys
+ '''
+ result = []
+ for i in range(n):
+ signing_key = rsa.generate_private_key(
+ public_exponent=65537, key_size=2048, backend=default_backend())
+ pem = signing_key.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.TraditionalOpenSSL,
+ encryption_algorithm=serialization.NoEncryption())
+ result.append(pem)
+ return result
+
+
+def generate_ed25519_keys(n):
+ '''
+ return a list of serializations of n ed25519 private keys
+ This list will be used for known keys
+ '''
+ result = []
+ for i in range(n):
+ signing_key = nacl.signing.SigningKey.generate()
+ result.append(signing_key.encode(encoder=nacl.encoding.HexEncoder))
+ return result
+
+
+def write_known_keys(file_name, n=64):
+ with open(file_name, 'w') as f:
+ f.write('rsa_known_keys_serialized = {}\n\n'.format(
+ generate_rsa_keys(n)))
+
+ f.write('ed25519_known_keys_serialized = {}'.format(
+ generate_ed25519_keys(n)))
+
+
+def load_rsa_key(signing_key):
+ return serialization.load_pem_private_key(
+ signing_key, password=None, backend=default_backend())
+
+
+def load_ed25519_key(signing_key):
+ return nacl.signing.SigningKey(signing_key, nacl.encoding.HexEncoder)
diff --git a/bin/crypto_conditions_test_gen/json_test_cases.py b/bin/crypto_conditions_test_gen/json_test_cases.py
new file mode 100644
index 00000000000..c7b6d0ae009
--- /dev/null
+++ b/bin/crypto_conditions_test_gen/json_test_cases.py
@@ -0,0 +1,515 @@
+test_cases = [
+ {
+ "json": {
+ "type": "preimage-sha-256",
+ "preimage": ""
+ },
+ "cost": 0,
+ "subtypes": [],
+ "fingerprintContents": "",
+ "fulfillment": "A0028000",
+ "conditionBinary": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
+ "conditionUri": "ni:///sha-256;47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU?fpt=preimage-sha-256&cost=0",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "",
+ "subfulfillment": {
+ "type": "preimage-sha-256",
+ "preimage": ""
+ }
+ },
+ "cost": 1024,
+ "subtypes": [
+ "preimage-sha-256"
+ ],
+ "fingerprintContents": "302E8000810100A227A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
+ "fulfillment": "A10B8000810100A204A0028000",
+ "conditionBinary": "A12A8020BB1AC5260C0141B7E54B26EC2330637C5597BF811951AC09E744AD20FF77E2878102040082020780",
+ "conditionUri": "ni:///sha-256;uxrFJgwBQbflSybsIzBjfFWXv4EZUawJ50StIP934oc?fpt=prefix-sha-256&cost=1024&subtypes=preimage-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 1,
+ "conditionIndexes": [1],
+ "subfulfillments": [
+ {
+ "type": "preimage-sha-256",
+ "preimage": ""
+ }
+ ]
+ },
+ "cost": 1024,
+ "subtypes": [
+ "preimage-sha-256"
+ ],
+ "fingerprintContents": "302C800101A127A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
+ "fulfillment": "A208A004A0028000A100",
+ "conditionBinary": "A22A8020B4B84136DF48A71D73F4985C04C6767A778ECB65BA7023B4506823BEEE7631B98102040082020780",
+ "conditionUri": "ni:///sha-256;tLhBNt9Ipx1z9JhcBMZ2eneOy2W6cCO0UGgjvu52Mbk?fpt=threshold-sha-256&cost=1024&subtypes=preimage-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "rsa-sha-256",
+ "modulus": "4e-LJNb3awnIHtd1KqJi8ETwSodNQ4CdMc6mEvmbDJeotDdBU-Pu89ZmFoQ-DkHCkyZLcbYXPbHPDWzVWMWGV3Bvzwl_cExIPlnL_f1bPue8gNdAxeDwR_PoX8DXWBV3am8_I8XcXnlxOaaILjgzakpfs2E3Yg_zZj264yhHKAGGL3Ly-HsgK5yJrdfNWwoHb3xT41A59n7RfsgV5bQwXMYxlwaNXm5Xm6beX04-V99eTgcv8s5MZutFIzlzh1J1ljnwJXv1fb1cRD-1FYzOCj02rce6AfM6C7bbsr-YnWBxEvI0TZk-d-VjwdNh3t9X2pbvLPxoXwArY4JGpbMJuQ",
+ "signature": "vULWVp9lma7UVflrwO0I7RSAvzbNnhRn-cb3RGHJ46dJM0svZASqX59rr-dsNH0GklCzXRyXDHkwWe5zOoGT8w-nj-x8rkWePd_XYzgF1HaUDQy1PX-zidza6vboz0jEtWNUMOTyvN_lBcLA_Be0DZPH7bfCYev0OJWnBeAkqgVJpmD3CjIVBkdSLb5rY1IEl8_4-NXXR2iifFuG5YC-P83Jbxl2KTy6DVjfxgtRi2MqbcHpUMQ-Ix_ho3mqbdzFLHDt-FHGwBI6lkJhz9s4V81s1a3DfY2izJJO2uHYTPYSRYfydMH6NpfaKQHwJp8DskPAO2FOA4Xhlh-sUAD5uw"
+ },
+ "cost": 65536,
+ "subtypes": [],
+ "fingerprintContents": "3082010480820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B9",
+ "fulfillment": "A382020880820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B981820100BD42D6569F6599AED455F96BC0ED08ED1480BF36CD9E1467F9C6F74461C9E3A749334B2F6404AA5F9F6BAFE76C347D069250B35D1C970C793059EE733A8193F30FA78FEC7CAE459E3DDFD7633805D476940D0CB53D7FB389DCDAEAF6E8CF48C4B5635430E4F2BCDFE505C2C0FC17B40D93C7EDB7C261EBF43895A705E024AA0549A660F70A32150647522DBE6B63520497CFF8F8D5D74768A27C5B86E580BE3FCDC96F1976293CBA0D58DFC60B518B632A6DC1E950C43E231FE1A379AA6DDCC52C70EDF851C6C0123A964261CFDB3857CD6CD5ADC37D8DA2CC924EDAE1D84CF6124587F274C1FA3697DA2901F0269F03B243C03B614E0385E1961FAC5000F9BB",
+ "conditionBinary": "A3278020B31FA8206E4EA7E515337B3B33082B877651801085ED84FB4DAEB247BF698D7F8103010000",
+ "conditionUri": "ni:///sha-256;sx-oIG5Op-UVM3s7Mwgrh3ZRgBCF7YT7Ta6yR79pjX8?fpt=rsa-sha-256&cost=65536",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw"
+ },
+ "cost": 131072,
+ "subtypes": [],
+ "fingerprintContents": "30228020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A",
+ "fulfillment": "A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100B",
+ "conditionBinary": "A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000",
+ "conditionUri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "preimage-sha-256",
+ "preimage": "YWFh"
+ },
+ "cost": 3,
+ "subtypes": [],
+ "fingerprintContents": "616161",
+ "fulfillment": "A0058003616161",
+ "conditionBinary": "A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103",
+ "conditionUri": "ni:///sha-256;mDSHbc-wXLFnpcJJU-uljErImxrfV_KPL50JrxB-6PA?fpt=preimage-sha-256&cost=3",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ },
+ "cost": 132099,
+ "subtypes": [
+ "ed25519-sha-256"
+ ],
+ "fingerprintContents": "30338003616161810100A229A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000",
+ "fulfillment": "A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509",
+ "conditionBinary": "A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308",
+ "conditionUri": "ni:///sha-256;RR_hXxYpnUlZk_5pLbmJ5WpSMKkEdvdzkqPNMhPAcz8?fpt=prefix-sha-256&cost=132099&subtypes=ed25519-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 3,
+ "prefix": "YmJi",
+ "subfulfillment": {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 6,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "pCNg9H99uG218DfIECQiNyB9et1uPlMX4hKyB-Jb7SrLSFrQvLtXdVcmDsu71ncY1sq630W61lXRuM6EYJ6XAQ"
+ }
+ }
+ },
+ "cost": 133135,
+ "subtypes": [
+ "ed25519-sha-256"
+ ],
+ "fingerprintContents": "30378003626262810103A22DA12B80207F19C9BB3BC767DE39657E11D16068F8CAB00E3E3C23916DF967B584A28B26DC810302040982020308",
+ "fulfillment": "A17C8003626262810103A272A1708003616161810106A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140A42360F47F7DB86DB5F037C810242237207D7ADD6E3E5317E212B207E25BED2ACB485AD0BCBB577557260ECBBBD67718D6CABADF45BAD655D1B8CE84609E9701",
+ "conditionBinary": "A12B8020177350AD8566C528B92D9B5382DF2C68D9BA9F9FA41D43DBDD8E40B118DD9641810302080F82020308",
+ "conditionUri": "ni:///sha-256;F3NQrYVmxSi5LZtTgt8saNm6n5-kHUPb3Y5AsRjdlkE?fpt=prefix-sha-256&cost=133135&subtypes=ed25519-sha-256",
+ "message": "7A7A7A"
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 2,
+ "conditionIndexes": [0],
+ "subfulfillments": [
+ {
+ "type": "rsa-sha-256",
+ "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls",
+ "signature": "QSAOQpYe6kYMgnbyOatmt2-PKGnqzMX56ZRcCrxjsUMGVvPaLSF6QzJ-6M4RJ9FKhddK9BL-k3A0W6a0QAPXca7dK6D8JQYn_fYWMa_WTpvucUsbr7afkBvutvlZsWpUsyippnTIg8r8T8L3sEdtvKqNm_1H9zTjRza_ZulzsxXCq_RyewYso8QCk261CEnhgV3RYEpgTe-maM_oI7AIv4979XzrG0HlGP9TrpEe-YgFcbPRdCNB1g48sr65PrXxePjve6DG8ikLmVXYPWD5Q9ZEVx7I1fBk2V8doedZ1pINhKVMvEl4NkCnsM89Ao5ifsetJakV9tjCSy4JkVKKAsW-8BBsGLPznFmZVut6J7M3MT3Pk-dYZ2BqSsWhq2-btETcHa0fVG-u3mrpNIhASQF2kTcdUe4dZQ785YGzDGhDCH5wFBkPRGAmfYlNUlgU3aNDuaBIncCMajQzR0LOowtJJRLSlXRSF91l7P7LnKGNIS-EpQbXACbtMyiWvSSmMFoMve6d_Yq7hQf4QOuUz6jpKOaRDCdfe2jRH5ZGPBAqWWYTR4aVvHZx-ycwkTnGzS8Vyn4kBS5H9ONO-LhE7DZKhb3JvoQlz_cqd76Y2QFphqZnEJgl8e8Y4IFwqHtDaOfBQsGdrRBgXE80HVIJmHrzGrqOLklj8-B5TdEfC3I"
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "NEydEqAqV7Nf2WYZPO6V1du7xndVP86_QUwY2nUAKd05xuU63Yks7-RCNYMf-OXzaGiO93zPb5l_1BGmp-v5Ag"
+ }
+ },
+ {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ ]
+ },
+ "cost": 397315,
+ "subtypes": [
+ "ed25519-sha-256",
+ "prefix-sha-256",
+ "rsa-sha-256"
+ ],
+ "fingerprintContents": "308184800102A17FA12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000",
+ "fulfillment": "A2820106A081D8A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140344C9D12A02A57B35FD966193CEE95D5DBBBC677553FCEBF414C18DA750029DD39C6E53ADD892CEFE44235831FF8E5F368688EF77CCF6F997FD411A6A7EBF902A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A129A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000",
+ "conditionBinary": "A22B8020B6ACF4083E438BE4356F25FF92C295E9C8E1BAB141B4607BA48511EBA35AEFCC810306100382020358",
+ "conditionUri": "ni:///sha-256;tqz0CD5Di-Q1byX_ksKV6cjhurFBtGB7pIUR66Na78w?fpt=threshold-sha-256&cost=397315&subtypes=ed25519-sha-256,prefix-sha-256,rsa-sha-256",
+ "message": "616161"
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 1,
+ "conditionIndexes": [0, 1, 2, 3],
+ "subfulfillments": [
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ },
+ {
+ "type": "rsa-sha-256",
+ "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls",
+ "signature": "fss4we3Ml_SM2IUwwkbFSRKGf1EbxN3QjFc8ogg6yf2qNFwXdo2t6YNCSBjbUqNy4oD7pWgvZSFalz-3KGdJO2T4-zQVM5felzo2xzxSuR4DQBtvpWP3d4T6r4ggQFvukx6jX3qNlCUUrlhsH8yo7e-biQTRuOEcnNjdu38VG3cX4STnLVejyjP0TkrXvDvYf9wge6rqrbax8XVPx48bQmbd84bl1STBo4cGUjqoIPEYsFN3Law-SoZ700WkvDWSCe9sHIPPVDKcJUjRrwUd_tr1PoBA9_OwYThrJ7UMj98w-k_X1fPfTbB0RMW4Z9V_P9L5r0AXRyHlYEJILwy4oEx4b74N47mHskntvTC32yuSic9E0OfLLmvewIpukErdNo5AwKf5LoSGWAaLDdbF7989xvK3YBB8AfWqr6jjf3ZmKIb8XS2yXduOP9T6ixFPj-pjn0VfAugCY_a85IO6LiChBeliMO84t5OO-qUKmdnj1qMd5Piy_H28FnXN_57X6tSUmOfo1oNTvMKBCWBg5azgw5dZNCYu18OxfmpxsXPgWF8Th7omsxSFqCCOOBoFPoFdVu3Lj_2pa9pLgJF5FzWZKKNEBnwBjPf6MD_PkybcjX9atigIbUvTgEbwKVNZrgENagDTgT984cfm7baOB4eYWTuaV6ycCgpMLSgk-gA"
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ },
+ {
+ "type": "rsa-sha-256",
+ "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls",
+ "signature": "fss4we3Ml_SM2IUwwkbFSRKGf1EbxN3QjFc8ogg6yf2qNFwXdo2t6YNCSBjbUqNy4oD7pWgvZSFalz-3KGdJO2T4-zQVM5felzo2xzxSuR4DQBtvpWP3d4T6r4ggQFvukx6jX3qNlCUUrlhsH8yo7e-biQTRuOEcnNjdu38VG3cX4STnLVejyjP0TkrXvDvYf9wge6rqrbax8XVPx48bQmbd84bl1STBo4cGUjqoIPEYsFN3Law-SoZ700WkvDWSCe9sHIPPVDKcJUjRrwUd_tr1PoBA9_OwYThrJ7UMj98w-k_X1fPfTbB0RMW4Z9V_P9L5r0AXRyHlYEJILwy4oEx4b74N47mHskntvTC32yuSic9E0OfLLmvewIpukErdNo5AwKf5LoSGWAaLDdbF7989xvK3YBB8AfWqr6jjf3ZmKIb8XS2yXduOP9T6ixFPj-pjn0VfAugCY_a85IO6LiChBeliMO84t5OO-qUKmdnj1qMd5Piy_H28FnXN_57X6tSUmOfo1oNTvMKBCWBg5azgw5dZNCYu18OxfmpxsXPgWF8Th7omsxSFqCCOOBoFPoFdVu3Lj_2pa9pLgJF5FzWZKKNEBnwBjPf6MD_PkybcjX9atigIbUvTgEbwKVNZrgENagDTgT984cfm7baOB4eYWTuaV6ycCgpMLSgk-gA"
+ },
+ {
+ "type": "preimage-sha-256",
+ "preimage": "YWFh"
+ }
+ ]
+ },
+ "cost": 267264,
+ "subtypes": [
+ "ed25519-sha-256",
+ "prefix-sha-256",
+ "preimage-sha-256",
+ "rsa-sha-256"
+ ],
+ "fingerprintContents": "3081D9800101A181D3A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000",
+ "fulfillment": "A281B8A007A0058003616161A181ACA12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000",
+ "conditionBinary": "A22B80209A0B2C63DF80686E6020D0CA21CBFE668CCEC3D1AF82713FEAE9B8DD4A0F9BB78103041400820203D8",
+ "conditionUri": "ni:///sha-256;mgssY9-AaG5gINDKIcv-ZozOw9GvgnE_6um43UoPm7c?fpt=threshold-sha-256&cost=267264&subtypes=ed25519-sha-256,prefix-sha-256,preimage-sha-256,rsa-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 4,
+ "subfulfillments": [
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ },
+ {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw"
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ },
+ {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw"
+ }
+ ]
+ },
+ "cost": 530438,
+ "subtypes": [
+ "ed25519-sha-256",
+ "prefix-sha-256"
+ ],
+ "fingerprintContents": "3081B2800104A181ACA12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A12B8020451FE15F16299D495993FE692DB989E56A5230A90476F77392A3CD3213C0733F810302040382020308A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000",
+ "fulfillment": "A28201B6A08201B0A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100BA4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100BA100",
+ "conditionBinary": "A22B80208E433EF5D3EAA00A2B34A05CA7C22DD392973A19F1A243268CB53111BDF1C844810308180682020348",
+ "conditionUri": "ni:///sha-256;jkM-9dPqoAorNKBcp8It05KXOhnxokMmjLUxEb3xyEQ?fpt=threshold-sha-256&cost=530438&subtypes=ed25519-sha-256,prefix-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 2,
+ "subfulfillments": [
+ {
+ "type": "threshold-sha-256",
+ "threshold": 2,
+ "conditionIndexes": [0],
+ "subfulfillments": [
+ {
+ "type": "rsa-sha-256",
+ "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls",
+ "signature": "fss4we3Ml_SM2IUwwkbFSRKGf1EbxN3QjFc8ogg6yf2qNFwXdo2t6YNCSBjbUqNy4oD7pWgvZSFalz-3KGdJO2T4-zQVM5felzo2xzxSuR4DQBtvpWP3d4T6r4ggQFvukx6jX3qNlCUUrlhsH8yo7e-biQTRuOEcnNjdu38VG3cX4STnLVejyjP0TkrXvDvYf9wge6rqrbax8XVPx48bQmbd84bl1STBo4cGUjqoIPEYsFN3Law-SoZ700WkvDWSCe9sHIPPVDKcJUjRrwUd_tr1PoBA9_OwYThrJ7UMj98w-k_X1fPfTbB0RMW4Z9V_P9L5r0AXRyHlYEJILwy4oEx4b74N47mHskntvTC32yuSic9E0OfLLmvewIpukErdNo5AwKf5LoSGWAaLDdbF7989xvK3YBB8AfWqr6jjf3ZmKIb8XS2yXduOP9T6ixFPj-pjn0VfAugCY_a85IO6LiChBeliMO84t5OO-qUKmdnj1qMd5Piy_H28FnXN_57X6tSUmOfo1oNTvMKBCWBg5azgw5dZNCYu18OxfmpxsXPgWF8Th7omsxSFqCCOOBoFPoFdVu3Lj_2pa9pLgJF5FzWZKKNEBnwBjPf6MD_PkybcjX9atigIbUvTgEbwKVNZrgENagDTgT984cfm7baOB4eYWTuaV6ycCgpMLSgk-gA"
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "YWFh",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ }
+ },
+ {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "5VZDAMNgrHKQhuLMgG6CioSHfx645dl02HPgZSJJAVVfuIIVkKM7rMYeOXAc-bRr0lv18FlbviRlUUFDjnoQCw"
+ }
+ ]
+ },
+ {
+ "type": "preimage-sha-256",
+ "preimage": "YWFh"
+ }
+ ]
+ },
+ "cost": 399366,
+ "subtypes": [
+ "ed25519-sha-256",
+ "prefix-sha-256",
+ "preimage-sha-256",
+ "rsa-sha-256"
+ ],
+ "fingerprintContents": "3059800102A154A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103A22B8020B6ACF4083E438BE4356F25FF92C295E9C8E1BAB141B4607BA48511EBA35AEFCC810306100382020358",
+ "fulfillment": "A2820117A0820111A0058003616161A2820106A081D8A1708003616161810100A266A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E5564300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E39701CF9B46BD25BF5F0595BBE24655141438E7A100BA129A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000A100",
+ "conditionBinary": "A22B80200C99630A201A99B0748D2BADB205E5CA939692C687D1C4A697E39BA8BA1EBE718103061806820203D8",
+ "conditionUri": "ni:///sha-256;DJljCiAambB0jSutsgXlypOWksaH0cSml-ObqLoevnE?fpt=threshold-sha-256&cost=399366&subtypes=ed25519-sha-256,prefix-sha-256,preimage-sha-256,rsa-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 1,
+ "conditionIndexes": [0],
+ "subfulfillments": [
+ {
+ "type": "preimage-sha-256",
+ "preimage": "YWFh"
+ },
+ {
+ "type": "preimage-sha-256",
+ "preimage": "YWFh"
+ }
+ ]
+ },
+ "cost": 2051,
+ "subtypes": [
+ "preimage-sha-256"
+ ],
+ "fingerprintContents": "3053800101A14EA02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103",
+ "fulfillment": "A232A007A0058003616161A127A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103",
+ "conditionBinary": "A22A8020E4FDB4652C6F17A38B2ABE9AA00640B1E184FE7A8D0C971B5D24F7EDA6FC68BF8102080382020780",
+ "conditionUri": "ni:///sha-256;5P20ZSxvF6OLKr6aoAZAseGE_nqNDJcbXST37ab8aL8?fpt=threshold-sha-256&cost=2051&subtypes=preimage-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "rsa-sha-256",
+ "modulus": "4e-LJNb3awnIHtd1KqJi8ETwSodNQ4CdMc6mEvmbDJeotDdBU-Pu89ZmFoQ-DkHCkyZLcbYXPbHPDWzVWMWGV3Bvzwl_cExIPlnL_f1bPue8gNdAxeDwR_PoX8DXWBV3am8_I8XcXnlxOaaILjgzakpfs2E3Yg_zZj264yhHKAGGL3Ly-HsgK5yJrdfNWwoHb3xT41A59n7RfsgV5bQwXMYxlwaNXm5Xm6beX04-V99eTgcv8s5MZutFIzlzh1J1ljnwJXv1fb1cRD-1FYzOCj02rce6AfM6C7bbsr-YnWBxEvI0TZk-d-VjwdNh3t9X2pbvLPxoXwArY4JGpbMJuQ",
+ "signature": "SOiUXv4AdVbVv01fJJ5ICPcwfilRHTJi2u9h2ICY-apKi8BiOoyXVzj2XWv0WdVD8onXPLx69Oo6M_vz7ERARHkR1yKUCR5WGDNijkmncu1gjebERZWpHj4X1s9ew7JSjWPSrdZGOYmxLuxXffZHCWDfaDKp2Ew2DRwhetZMhiW9tZT7CtoIbN7LveWA1CS_l0bS8MMSgm27sArWi1LEy31HFWujXjqYHJc4Y3ksyA0EoYAhClJBWGW2Szphd0sdOXXXipiwgh7lXKD4YwXUJSnhDrAVzv1AL7WbKruN7uUqbyRH0ihGA9IZzU6M-c_91UmIicN4C1ndalfvfXMmIA"
+ },
+ "cost": 65536,
+ "subtypes": [],
+ "fingerprintContents": "3082010480820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B9",
+ "fulfillment": "A382020880820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C483E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67ED17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB452339738752759639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B98182010048E8945EFE007556D5BF4D5F249E4808F7307E29511D3262DAEF61D88098F9AA4A8BC0623A8C975738F65D6BF459D543F289D73CBC7AF4EA3A33FBF3EC4440447911D72294091E561833628E49A772ED608DE6C44595A91E3E17D6CF5EC3B2528D63D2ADD6463989B12EEC577DF6470960DF6832A9D84C360D1C217AD64C8625BDB594FB0ADA086CDECBBDE580D424BF9746D2F0C312826DBBB00AD68B52C4CB7D47156BA35E3A981C973863792CC80D04A180210A52415865B64B3A61774B1D3975D78A98B0821EE55CA0F86305D42529E10EB015CEFD402FB59B2ABB8DEEE52A6F2447D2284603D219CD4E8CF9CFFDD5498889C3780B59DD6A57EF7D732620",
+ "conditionBinary": "A3278020B31FA8206E4EA7E515337B3B33082B877651801085ED84FB4DAEB247BF698D7F8103010000",
+ "conditionUri": "ni:///sha-256;sx-oIG5Op-UVM3s7Mwgrh3ZRgBCF7YT7Ta6yR79pjX8?fpt=rsa-sha-256&cost=65536",
+ "message": "616161"
+ },
+ {
+ "json": {
+ "type": "rsa-sha-256",
+ "modulus": "u7ChoxT_ai_HmuBvYQ3I1EIckz7QxDVOaurZHvlH6k-PYysuL3i4oHp4wzJIlcfLCRbrM0wQCQ5e254Cle0v8d6v0fSfVEEuQ-3iywbP_DlTAzCaTOPqwjQdmvMYgzfuur09Skr49D4VfW8C-dHySkJ3cEnJMKojP-1cY7B_clzea7JEBPy_wLhyR-rLfbBEeFumbt6OeSESRwFQQB4KhHG9428tXsuWD1cboX2vOB2DeN7CHhAS5LN2yebEa7Z9aO7xK6mhWWf0hti8kbPisG-l_KabdSQmrwKcsUnqWG3thR66Fgh2rNhfBiJx-tc9FfXw8CLiYTCSJr7jVnrQUvl0bKi_rPDU9BcwCFwJegICgwHZKgxUXAOXRywu7lthIiAl1jRw7mgc4yUnR86coj8Z5m8vY4bmrRORHXrazTjOXKj_LC2Zq7D1w7qEdQlhPmYyoSzm73jaTIIOkINAUwA9EZf2pXsBAP7hUshMixszu5ZXGYh4D6-9aXz4NwqZ2o-q91aH2VHMZTPHi44c4uHVzrmhFikgGvQ3R1yUAn-lJhS0MAt33PGArEnKo0AWjzJi_R7osTgCzqNXVLQjuDP6FMXdDEdt3l1ee_c3TWHySMO6uRywVQvRy-9wUH7o2xzzmTB-Io1PRZKmbFhXPP7MY5ZoBvr4gQnMsJkj6ls",
+ "signature": "QSAOQpYe6kYMgnbyOatmt2-PKGnqzMX56ZRcCrxjsUMGVvPaLSF6QzJ-6M4RJ9FKhddK9BL-k3A0W6a0QAPXca7dK6D8JQYn_fYWMa_WTpvucUsbr7afkBvutvlZsWpUsyippnTIg8r8T8L3sEdtvKqNm_1H9zTjRza_ZulzsxXCq_RyewYso8QCk261CEnhgV3RYEpgTe-maM_oI7AIv4979XzrG0HlGP9TrpEe-YgFcbPRdCNB1g48sr65PrXxePjve6DG8ikLmVXYPWD5Q9ZEVx7I1fBk2V8doedZ1pINhKVMvEl4NkCnsM89Ao5ifsetJakV9tjCSy4JkVKKAsW-8BBsGLPznFmZVut6J7M3MT3Pk-dYZ2BqSsWhq2-btETcHa0fVG-u3mrpNIhASQF2kTcdUe4dZQ785YGzDGhDCH5wFBkPRGAmfYlNUlgU3aNDuaBIncCMajQzR0LOowtJJRLSlXRSF91l7P7LnKGNIS-EpQbXACbtMyiWvSSmMFoMve6d_Yq7hQf4QOuUz6jpKOaRDCdfe2jRH5ZGPBAqWWYTR4aVvHZx-ycwkTnGzS8Vyn4kBS5H9ONO-LhE7DZKhb3JvoQlz_cqd76Y2QFphqZnEJgl8e8Y4IFwqHtDaOfBQsGdrRBgXE80HVIJmHrzGrqOLklj8-B5TdEfC3I"
+ },
+ "cost": 262144,
+ "subtypes": [],
+ "fingerprintContents": "3082020480820200BBB0A1A314FF6A2FC79AE06F610DC8D4421C933ED0C4354E6AEAD91EF947EA4F8F632B2E2F78B8A07A78C3324895C7CB0916EB334C10090E5EDB9E0295ED2FF1DEAFD1F49F54412E43EDE2CB06CFFC395303309A4CE3EAC2341D9AF3188337EEBABD3D4A4AF8F43E157D6F02F9D1F24A42777049C930AA233FED5C63B07F725CDE6BB24404FCBFC0B87247EACB7DB044785BA66EDE8E792112470150401E0A8471BDE36F2D5ECB960F571BA17DAF381D8378DEC21E1012E4B376C9E6C46BB67D68EEF12BA9A15967F486D8BC91B3E2B06FA5FCA69B752426AF029CB149EA586DED851EBA160876ACD85F062271FAD73D15F5F0F022E261309226BEE3567AD052F9746CA8BFACF0D4F41730085C097A02028301D92A0C545C0397472C2EEE5B61222025D63470EE681CE3252747CE9CA23F19E66F2F6386E6AD13911D7ADACD38CE5CA8FF2C2D99ABB0F5C3BA847509613E6632A12CE6EF78DA4C820E90834053003D1197F6A57B0100FEE152C84C8B1B33BB96571988780FAFBD697CF8370A99DA8FAAF75687D951CC6533C78B8E1CE2E1D5CEB9A11629201AF437475C94027FA52614B4300B77DCF180AC49CAA340168F3262FD1EE8B13802CEA35754B423B833FA14C5DD0C476DDE5D5E7BF7374D61F248C3BAB91CB0550BD1CBEF70507EE8DB1CF399307E228D4F4592A66C58573CFECC63966806FAF88109CCB09923EA5B",
+ "fulfillment": "A382040880820200BBB0A1A314FF6A2FC79AE06F610DC8D4421C933ED0C4354E6AEAD91EF947EA4F8F632B2E2F78B8A07A78C3324895C7CB0916EB334C10090E5EDB9E0295ED2FF1DEAFD1F49F54412E43EDE2CB06CFFC395303309A4CE3EAC2341D9AF3188337EEBABD3D4A4AF8F43E157D6F02F9D1F24A42777049C930AA233FED5C63B07F725CDE6BB24404FCBFC0B87247EACB7DB044785BA66EDE8E792112470150401E0A8471BDE36F2D5ECB960F571BA17DAF381D8378DEC21E1012E4B376C9E6C46BB67D68EEF12BA9A15967F486D8BC91B3E2B06FA5FCA69B752426AF029CB149EA586DED851EBA160876ACD85F062271FAD73D15F5F0F022E261309226BEE3567AD052F9746CA8BFACF0D4F41730085C097A02028301D92A0C545C0397472C2EEE5B61222025D63470EE681CE3252747CE9CA23F19E66F2F6386E6AD13911D7ADACD38CE5CA8FF2C2D99ABB0F5C3BA847509613E6632A12CE6EF78DA4C820E90834053003D1197F6A57B0100FEE152C84C8B1B33BB96571988780FAFBD697CF8370A99DA8FAAF75687D951CC6533C78B8E1CE2E1D5CEB9A11629201AF437475C94027FA52614B4300B77DCF180AC49CAA340168F3262FD1EE8B13802CEA35754B423B833FA14C5DD0C476DDE5D5E7BF7374D61F248C3BAB91CB0550BD1CBEF70507EE8DB1CF399307E228D4F4592A66C58573CFECC63966806FAF88109CCB09923EA5B8182020041200E42961EEA460C8276F239AB66B76F8F2869EACCC5F9E9945C0ABC63B1430656F3DA2D217A43327EE8CE1127D14A85D74AF412FE9370345BA6B44003D771AEDD2BA0FC250627FDF61631AFD64E9BEE714B1BAFB69F901BEEB6F959B16A54B328A9A674C883CAFC4FC2F7B0476DBCAA8D9BFD47F734E34736BF66E973B315C2ABF4727B062CA3C402936EB50849E1815DD1604A604DEFA668CFE823B008BF8F7BF57CEB1B41E518FF53AE911EF9880571B3D1742341D60E3CB2BEB93EB5F178F8EF7BA0C6F2290B9955D83D60F943D644571EC8D5F064D95F1DA1E759D6920D84A54CBC49783640A7B0CF3D028E627EC7AD25A915F6D8C24B2E0991528A02C5BEF0106C18B3F39C599956EB7A27B337313DCF93E75867606A4AC5A1AB6F9BB444DC1DAD1F546FAEDE6AE934884049017691371D51EE1D650EFCE581B30C6843087E7014190F4460267D894D525814DDA343B9A0489DC08C6A34334742CEA30B492512D295745217DD65ECFECB9CA18D212F84A506D70026ED332896BD24A6305A0CBDEE9DFD8ABB8507F840EB94CFA8E928E6910C275F7B68D11F96463C102A596613478695BC7671FB27309139C6CD2F15CA7E24052E47F4E34EF8B844EC364A85BDC9BE8425CFF72A77BE98D9016986A667109825F1EF18E08170A87B4368E7C142C19DAD10605C4F341D5209987AF31ABA8E2E4963F3E0794DD11F0B72",
+ "conditionBinary": "A32780204DD2EA7F85B3EACB8F19058E8360955C32E74C124392A1F44660739709C539C38103040000",
+ "conditionUri": "ni:///sha-256;TdLqf4Wz6suPGQWOg2CVXDLnTBJDkqH0RmBzlwnFOcM?fpt=rsa-sha-256&cost=262144",
+ "message": "616161"
+ },
+ {
+ "json": {
+ "type": "ed25519-sha-256",
+ "publicKey": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo",
+ "signature": "UGoepoMY5i1AY12tBD4Zh-vCbltcRAb3vfhacziPv-XCRaxJ9HcOvHh3CCcKpqh2n-_okw_Q6h7mSzFAfXaVCQ"
+ },
+ "cost": 131072,
+ "subtypes": [],
+ "fingerprintContents": "30228020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A",
+ "fulfillment": "A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140506A1EA68318E62D40635DAD043E1987EBC26E5B5C4406F7BDF85A73388FBFE5C245AC49F4770EBC787708270AA6A8769FEFE8930FD0EA1EE64B31407D769509",
+ "conditionBinary": "A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F8103020000",
+ "conditionUri": "ni:///sha-256;eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8?fpt=ed25519-sha-256&cost=131072",
+ "message": "616161"
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 2,
+ "subfulfillments": [
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "aHR0cHM6Ly9ub3RhcnkuZXhhbXBsZS9jYXNlcy82NTdjMTJkYS04ZGNhLTQzYjAtOTdjYS04ZWU4YzM4YWI5Zjcvc3RhdGUvZXhlY3V0ZWQ",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "LlMeiL_oxBn5Ya2ckB3ivdjnoOcUhFUFnonreZhrJSQ",
+ "signature": "5f3bvsLo21m8tqaA5KcFb91GUAtOmaNxnyVzUDahSSg-KHzt7c2eWlDNl2q0EfhP9Wmqveb24M5qC90MSoJGCA"
+ }
+ },
+ {
+ "type": "preimage-sha-256",
+ "preimage": "aHR0cHM6Ly9ub3RhcnkuZXhhbXBsZS9jYXNlcy82NTdjMTJkYS04ZGNhLTQzYjAtOTdjYS04ZWU4YzM4YWI5Zjcvc3RhdGUvZXhlY3V0ZWQ"
+ }
+ ]
+ },
+ "cost": 134304,
+ "subtypes": [
+ "ed25519-sha-256",
+ "prefix-sha-256",
+ "preimage-sha-256"
+ ],
+ "fingerprintContents": "3059800102A154A02580200B4AC3A1E0932CB71B74309FAD7D15DF51BD4D1359ED59FF7C917B35DF24464A810150A12B80203F94525555CF4C5234BF77CB108501D97B9D8A28D1E7A3A7FE8D3D7F031FDEBD810302045082020308",
+ "fulfillment": "A282011AA0820114A052805068747470733A2F2F6E6F746172792E6578616D706C652F63617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564A181BD805068747470733A2F2F6E6F746172792E6578616D706C652F63617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564810100A266A46480202E531E88BFE8C419F961AD9C901DE2BDD8E7A0E7148455059E89EB79986B25248140E5FDDBBEC2E8DB59BCB6A680E4A7056FDD46500B4E99A3719F25735036A149283E287CEDEDCD9E5A50CD976AB411F84FF569AABDE6F6E0CE6A0BDD0C4A824608A100",
+ "conditionBinary": "A22B802009E391004628725E88F8557E954FB2A0EAE2B7C151C47DF3C4AF22F8C16988F98103020CA0820203C8",
+ "conditionUri": "ni:///sha-256;CeORAEYocl6I-FV-lU-yoOrit8FRxH3zxK8i-MFpiPk?fpt=threshold-sha-256&cost=134304&subtypes=ed25519-sha-256,prefix-sha-256,preimage-sha-256",
+ "message": ""
+ },
+ {
+ "json": {
+ "type": "threshold-sha-256",
+ "threshold": 2,
+ "subfulfillments": [
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 0,
+ "prefix": "Y2FzZXMvNjU3YzEyZGEtOGRjYS00M2IwLTk3Y2EtOGVlOGMzOGFiOWY3L3N0YXRlL2V4ZWN1dGVk",
+ "subfulfillment": {
+ "type": "threshold-sha-256",
+ "threshold": 3,
+ "conditionIndexes": [0],
+ "subfulfillments": [
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 1025,
+ "prefix": "aHR0cHM6Ly9ub3Rhcnk0LmV4YW1wbGUv",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "Rkvo5cq-MB4FMv5iIUjPy8zxG_MXop9cyHg5xpUf6pg",
+ "signature": "rlayC5_mc7O1szd9hCV8kIVronrH_IYj89BwVubxNmV2CacPp9ASs8UqqLrQPtxIKp7kA-aA5IPjr1mlxK05BA"
+ }
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 1024,
+ "prefix": "aHR0cHM6Ly9ub3RhcnkxLmV4YW1wbGUv",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "LlMeiL_oxBn5Ya2ckB3ivdjnoOcUhFUFnonreZhrJSQ",
+ "signature": "hzAaGAj3PCA_DpyBBvEwcQiB2s2sgHwQ00m3mCDcs0B8d7nSPbQoJ2QL3EE4P9xOynYZwXA36HA3pcfPM4F6Dg"
+ }
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 1024,
+ "prefix": "aHR0cHM6Ly9ub3RhcnkyLmV4YW1wbGUv",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "WQI-doqchYdsYeuqo07BjmSFf6dmksVamWNfm4jlr5A",
+ "signature": "rPnug4hbpY9ixCtImejOqRWpGS90iMFZLOlZVgtS-Ho3kOA208aVS4dVQUjRMcy682nGimajE3_o-kNooWWgCg"
+ }
+ },
+ {
+ "type": "prefix-sha-256",
+ "maxMessageLength": 1024,
+ "prefix": "aHR0cHM6Ly9ub3RhcnkzLmV4YW1wbGUv",
+ "subfulfillment": {
+ "type": "ed25519-sha-256",
+ "publicKey": "mpisbb_wkOluONgfBUd9-Gs7uw7_wxG8e0LNrJnWvdk",
+ "signature": "l6MrDGHOFRA2ytNZacn5XrVEZepdYpupZav4pqkX8Q3RSr5V0zBUQ45oyRWmtnwd34oMFtLYAfjQuoXv7pu_Dw"
+ }
+ }
+ ]
+ }
+ },
+ {
+ "type": "preimage-sha-256",
+ "preimage": "aHR0cHM6Ly9ub3RhcnkuZXhhbXBsZS9jYXNlcy82NTdjMTJkYS04ZGNhLTQzYjAtOTdjYS04ZWU4YzM4YWI5Zjcvc3RhdGUvZXhlY3V0ZWQ"
+ }
+ ]
+ },
+ "cost": 406738,
+ "subtypes": [
+ "ed25519-sha-256",
+ "prefix-sha-256",
+ "preimage-sha-256"
+ ],
+ "fingerprintContents": "3059800102A154A02580200B4AC3A1E0932CB71B74309FAD7D15DF51BD4D1359ED59FF7C917B35DF24464A810150A12B8020062F2C1BDD08661FE7FEFAC20E02DA8B0184FCD36F6C6C54C53CC28D2E54DD118103062C8282020328",
+ "fulfillment": "A2820272A082026CA052805068747470733A2F2F6E6F746172792E6578616D706C652F63617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564A1820214803963617365732F36353763313264612D386463612D343362302D393763612D3865653863333861623966372F73746174652F6578656375746564810100A28201D2A28201CEA082019BA18186801868747470733A2F2F6E6F74617279312E6578616D706C652F81020400A266A46480202E531E88BFE8C419F961AD9C901DE2BDD8E7A0E7148455059E89EB79986B2524814087301A1808F73C203F0E9C8106F130710881DACDAC807C10D349B79820DCB3407C77B9D23DB42827640BDC41383FDC4ECA7619C17037E87037A5C7CF33817A0EA18186801868747470733A2F2F6E6F74617279322E6578616D706C652F81020400A266A464802059023E768A9C85876C61EBAAA34EC18E64857FA76692C55A99635F9B88E5AF908140ACF9EE83885BA58F62C42B4899E8CEA915A9192F7488C1592CE959560B52F87A3790E036D3C6954B87554148D131CCBAF369C68A66A3137FE8FA4368A165A00AA18186801868747470733A2F2F6E6F74617279332E6578616D706C652F81020400A266A46480209A98AC6DBFF090E96E38D81F05477DF86B3BBB0EFFC311BC7B42CDAC99D6BDD9814097A32B0C61CE151036CAD35969C9F95EB54465EA5D629BA965ABF8A6A917F10DD14ABE55D33054438E68C915A6B67C1DDF8A0C16D2D801F8D0BA85EFEE9BBF0FA12DA12B8020EE0BC02F977C264B6C306ED1B168FEB4FD600950AD21750CE8A86ECBD4603538810302081982020308A100",
+ "conditionBinary": "A22B8020424A704949529267B621B3D79119D729B2382CED8B296C3C028FA97D350F6D0781030634D2820203C8",
+ "conditionUri": "ni:///sha-256;QkpwSUlSkme2IbPXkRnXKbI4LO2LKWw8Ao-pfTUPbQc?fpt=threshold-sha-256&cost=406738&subtypes=ed25519-sha-256,prefix-sha-256,preimage-sha-256",
+ "message": ""
+ }
+]
diff --git a/bin/crypto_conditions_test_gen/known_signing_keys.py b/bin/crypto_conditions_test_gen/known_signing_keys.py
new file mode 100644
index 00000000000..30a4d8c2748
--- /dev/null
+++ b/bin/crypto_conditions_test_gen/known_signing_keys.py
@@ -0,0 +1,3 @@
+rsa_known_keys_serialized = [b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAp2xvkhV8VuitA5orBGy2QQJKcxKHhsJYPNAEpIyYp3t5Ibre\nOMpn7920njeLZfVazv4W/a1ie6e6XiwQmUAw9u4mSSc7xbghMFw+RrYCFcQPGCTy\nyziLR3cxY3FI7naz/baioI5aAXMyNb+YOT4aq4fav4IMcS61ilTMf8K/tH/i5bkP\nbu5KL4sgseqq3IlZNHy6mzql4VdwZGjxu65A9viQY1V1OjqecWqTUI1i/a+Jih8n\nV90w08p9j6VZyk7k7J0lIKeBagamu7LxdflrKcd1wJ/Mh0IZn4u1K6aaw2GIdIKR\ndvVGjOeB9SwlbV1QGAPWHe2NdH8Frrs1uedv4QIDAQABAoIBAQCFPMNxk023cEW3\n9ynCxK0qASO0fguC0AkepED9nrIhnx6gHDGbKmEav+psezGhe2fAEO/FBAW6+JlV\nNmYjP7V33cB0ORfVWpp2/QHGjjQMlFGa2lSqK56TyiRwygfLVoYZJA46De9M9hsA\nMTB4ZZcd6OjJvTofnJd2tdNk3VxozI+KYLgOR9QqmnStG7XrDYQVEJ3VDJ0UZTzF\nzZJkREgwqSbdCHzpof30iMbrl5h67I/c1F1ydEq5x0G3WPwil2fU16D8Bxs3myBN\npZdYmX+tyDTA6IAL+jqsCT3FLQWDqWngRjxX3+0MaoquKtGh/5Kv5d6i2ZypXg6l\nDSWSbtJBAoGBANqTVvr754u+XPgMWk6m8ftzbsddbvIi2h2DDTXgzspVQJwsYjnx\nR97fa2Ewx2RDoD6ugiFMAvZPSb54a223qEoBLDvGcC6wPN2ifKHCmsnJt2gPKNpw\nVWQEm82xonJNDmn2AZ9xI78/u4kV4cvK/pPGdDspA8sghsaBx+os+1I5AoGBAMQW\n/Vn9/+8HWKPXTyG4sZinEhanDh4f8zlK2VXHm93wp46ja4631x7mSTfEWyIFWGPZ\niTY2uV7dQez8lGv1p2m4m2AW4vIdPI9n3Zia2Qpt6PXCUur60otQEGnuZsPt0PWP\nKtVgYgTRjLZVswdUkLXF+MpptVgdPzBA3W4KDmrpAoGBAInOES3GOjLRvz2oVBt4\nh+H34iUR1EwXCxtGL8MDTH0miMit9vP/Gkf65nFtVpjxRgue7G5fxRJT5POQanLP\nMuwNlzzMnJfjB0zZD+jQj9SfN+IEhMZLwpp/iVeDIh8cU/UcY+OJanc+kFcak7Rv\n9Fdo7IkMD9c2V6K52jfbSkyJAoGBAIDmf+rNbpVpZdpmKe2H5K7tfJWJgV1KMW/D\nBTU/PvXZQbwhLw4lSXNVfGH9YM7vZphUor8RoC99EN5Bdyu00zVLFlBHGy5/C5VV\neLTdbblOYOpUsH8pHIhZvEB2Z/V5+nfBgi276xMx0Ob3VPWHoIztM8uTS5M9vpDw\nwt6YG7wpAoGAOh9EeNtJwfouiWBXie87ByaceJv3X7Y9ip6TctHSlvzTiojiO6us\nfAPFFCQ5BkK1KE+qumxV7R6lrkfl9dAAkLqPWet/mpRkfDyV0aC/y2QQgWi7gyKm\n4mjiukSP27Brh9+eIB+ASPMQbD2ltWZR2nkCeY7DPeP4eh9L/Hxlwac=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAxBvJepaBzi7yU8DUqLmoEpJFBvH0zSd9/8Flda63xJhT2Ppt\nhmPYTvUg4p6WBDYMP6x9CUIREzAtf2As7Co0w9i6bRR1KFbcc2y31rqKo54JDqTz\na10SxuTdjLGYzd7Krf+GtgYlm3GEoJsZFIjQx1WZ4B4OOWd03/Yp+pK2u+bhHNnu\nZdoT7FBqEX6utKyFpcfLQ0I2czQxtgte8J6ASavHecWn4xY1OkjmwGnicDAgdEc+\nOlEBIWAVU0Bobuefc7aYO25QuLLnQpB3YdQibAs9ZuAbfbChqanqDvKr4DL0SUTL\nrmAd4azTNCsDl5gt2vjoC4GUmDq9bRcYQlgMowIDAQABAoIBADw9GNeKEddmvlMR\nwYEOfl0uBvwKKJs4DVEZc/QBNjuyWtckna4RT2l5jJ9v/T7P51VLw6CrZZlSTDd7\nMHFeyZR7J1DIXvCENfVSqOnRpTJ7IecYrhkFnvXvaUqFZx2d2P43poaVPsoPxaCb\n48lV+WOxnLGmKdJiQBlngR+SH4V57TJRPi4VQlIpnP54uJhhsSXtrbK5V/YsWkRF\nRA+Fv/pGb//i2rXQ16Or9FODCvpC+cujiNrZ1B1edOdj7cx2fr3jkGzUFV8yLnxT\ncSUM4DYRL9JY3sh7j76yKqVAzhDgiqwPtwKJ+e2ZJ011schWsaua2J9q5eY+azl0\neYSd5EkCgYEA7oQMxR2uWfRhdy0IMe1T0XNvfnhwboIKZgB1TyIAJIgS2QlzuucC\nwvh8QfDZh+DbH3KDpOhK9an/bWgcLwnF2uPptV2ignR7kf/2MnW4rWcjGErVTtLx\ngGZxwamFOiR71VcJc2StNJdYtyLTPN/LwrZm8YIaEE4JUnpbhYQMZQcCgYEA0nvt\nzHikLIcmVz0nG+BHwi1ZdkGeTsvPcB2dYeInYyTynmvOtQkNfTZYyCuGwlZJFnB/\nNwPvMXycDVyTnsFCvVix6Ppm+PCvj5KQ8SArT79KXsXj8sMBdRZ7rd2LNcPsLkMe\nK/4MIDGbMoFDPFWYE5Jik+PQQp5zK35QqgoH8IUCgYBw95oUwjoR4O1IxMtc2ksY\nqNPl0qVtsam7JfxpvnDS3KAQq6lknIVyUr6HfMuKz7T48APp1xupvxp7dxPiM37C\nbbADdpXBs+nS/KheveL6Ph8hIlBlRQOdDOhUETZIk38TTGXtgYGHpycdliD2dAjN\n8BZRzI0Lq+o+ZjVyx1gXgQKBgQCsh0axXoX7tcAYrBidgeZv5Ko7A6hWoDrlhHDX\npf9xBf0VG/swaz4yT1TQptTsALwh945fMwtiPejQDv01mOYyvDwAYxf6n5bYEWRb\nMWovEPZN3tlsyKkuJ4KzTjBonrK+bOeTWcbosKCs8kWBdAuWp1vEK20u3iA7G2vV\nJSnC3QKBgQDgsLDvvEcnxB282763/AGy6EUn8eL3En3qa+ihenBIBpoj4k7HtIUG\ncqg9DVQUhLiBuOsjfwfC4GQ0ew9cdIeXmaSpRPfW1ki/kY59mucQZ1vAaM0kToYm\n9nnZ5S+qsKxEjGFrzxbTdiRqqW6sPRPaohnOmPlMC9gQ0MMARjduWQ==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAuiw7ULa/+Q8d1zJMAV//Lyr2M9D76h+k8i0iihmVqRe3TxfP\nVc0aOl8Hc8yqIXBks6D0tzCjgjeTxlneG6EWkFoa9nOrksgv9G9c8iIdMPgD2Jtf\nc3KOX9U3S0Pa/oQhZ+jj15E/JB37HxJuy/y3Wwo1czvORDSOzVOkz6djc80xD+B1\njeSp3P7wyT0mr797Dw4XudBKMoBka1RzWlDHMVn5c3KlebrboRSNd2c+wFvsbwv3\nxe5apo1JY4G70fmeu+2yqRhgp+7rMKGSk+jYNJ6s1iP8f8vn/qfmQqx3EcBnd9Gq\nXu071aWNNHzZV0SnxUQuHudj2FMbmtlnAhMyYQIDAQABAoIBAAnyvyCrA0sdwax0\nJZeVN5+fnhRawHtcQYYnX0cd4t9sQwFY9l1RXmtsFIvdjeHHjboDidAYK0SUD7Nz\nreiwDPKdvf8ZYzuustFO+hp+NC0fs6TpeIpEvtLkI9ZDw/oBuX9B9JraShN1/qtE\nYuNHl8ynmP+VXIpMfK0AIThva2MOpoQLjG7kXnzd2xzpZ0UVzgLBJUbdVGAmEpB7\nC0q1Xrbi64wr8PJokxZprrSYQ6egpYgWtgqQ70jY0Txd2P9dY/eptYIwEGm2gxpy\nPO18q0uvUtcSXUZ9IICm5EqqNf/7Xq/9xWkIBKbj8YLKvQzZQ+jkDXexjpCQw01f\ni37YzrkCgYEA6YiIeHp2Fo1m34BK6ccJJg/GdQWYevUBv/m/OrkESMDY7ODsIO3H\n1CrkYDlbXtuUAs/ISAUwkz6qMBprQyFpgKzaBWzBvNf5wCiFL2DN3K2zNWcQZDf0\nOLWJ78GvGYIucIy4hGtUg8oetq4Mhj3haw8hedfPhSYjEufC6ZJwcLcCgYEAzBVN\nIniw/x4nDgMriTDbiJDQCjbg3/IFiOrQKEYFMH62YZUMW3A0tfRzWRUvYirvmEer\n609dAQaCTpAQFcS14uqMyuwazJ55RnKYHswP9cyc6uea8+MUbq4v4HWyBdNOzw1G\nqS60OmQU6ssis+pJmKlFxDt35+XYnWCRYeviracCgYA56mdV4aD5lNXHZeyuL3NA\nYjcofFvFWo+1iX14VbwPQuxJSrqkq/Ob1YtPpcnYK4J4dAlKeycBzU+toE+rlVSx\n2aw0HjWp3LIOir4E8u/644UHIGk7QXkquzLbJ+CB5fwYY327MVcDXBPj4CQxApWt\n1FHMAs5vSfb20E1RWcTS3wKBgAZNghxWbeDzJXGhV27dO2p0TniCSV+hMdqQpe0Y\nfICK6UcKO193j6ku64EMznHRsaSaUgzqXozxjoSunlCNyrA/XinGMO2w1z39vAT3\neGa28wGuNXLiyHbCmmfsOptDc2OhnVUMRDSpcpvtuhykV7GHSMhOrd1Tk+UIXnUP\nu+hxAoGAMg2aNowtlsfaYkXcpv+WoPvocgYYT8ugGLwRdebO830tNBs0wQ8l057x\n0rE7Fl4PlbPgY25joYjpwt0VCor3HIhK23RCZ1DoyTXWRhxilQRT/PicdPDELNbh\n+habC8ZlzNNm6S0NBvhlKXm6HpsDzTPf9uPdQsitt6Ojq7cgIK0=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAvdHH8LA6pVs+SY1OAFSJuYnNS0PeWfZ6Z1w6xs+CPzWczNrN\n05eGW+n2BVULJu8eiNW2uhQKsna5s0YMwIAXE2gj3OwQGPyqvrPEx6mEpk5cCGt7\nTIGReV2QBhW7di9cU2APrPN8ScVH7LPak4fBuc8stfCFrbQ4Z4jaPfoBt1TZQQt7\nignghH27ibL8C3A2k1ZizLT8+R83kps6TnytS6Z2b9rEL4NTvZOpdolT4U3uJxFv\nvCGtQp8p9gPd7PqheNLeKS7YOn/pm13rN7iwoGY/As0qbtMcpWXcc76TVJor+DKL\n6M6aWdAF67us/ExLLqwqww8K10av/SINDVTMLwIDAQABAoIBADGdZa9pwMp74PoE\nr0Cy2BThCgIESVgmbrQZjVtt3dN7xfJdtrL5xiq8KowWQIX6tnZ5tftJRcqD8eIE\nC009rcpqzATwxvxg/pg+EM9HZd66WJ/yyBWlQEsCw6Rzhb5MfstkLnS8Z944bKZY\nFGe/ckmI91zO84IFu0CEqtbMRCqQ1QivsRUmep1EiOR11ZSsLSNOPSZ8dnig6aOW\nCsyg0UUka0JHqSccIOF0j/APim2vDz0omn6z7SaSrodlXOCwYVrDSduaQ/W/pt03\nrM+PYFIlx5A5uCHpacgPK84GrtG+xqcF5KUJ9oof89yp5xVmcmFzfvCSfjME4oai\nhOy4CqECgYEA9q358wPjEW4ekmcOsAeuakVEHxKID/XV3BEhdRQQssRY4dmd9YiI\ngfPSYYPwxZ/irELgceaE/WyIu6Gm7S3/YZX5ox6EP7jJktSosOcknp8OTLD33e3I\nj/Sao/39ERMUnfwX/yA4H1BNYnh1D0YY9Ojez/uG3+NFtx5qnEtaq1kCgYEAxP3S\nSpx0lXcLtT2PnPJdfj4PY23BQER73bH15N7oOqQmkbUCcTzDw5Eu7FuxmG3udOil\nrwb7hvDH9KZhXCMAXCTk0X1wSeXvqDYBvlD+ei0Cjsn32GP3RpQlVpH+aP5G/adF\nNOVNWSvnwOG/2ZcIglxPfF+tAm5LMYv/cykFKscCgYBViFWWkc52CLzI/rr1BnTP\nnFzWeVJ8Vj1lJPt44RA5LCO1jANdU44hoS1daqZdn+a03Ct+0vNh+/QdOxvqOXzH\nlX0NtlMc1THvm5UyYbMs3PHUXbhUV1Knt6S3ICZTeU44yWR4re9HzExIzIOqXB/j\nK6u2afyGPQO7TuygbPBvYQKBgQCzvPhkVW7WdYF8RAhGZqnBpzR5P0qFrjbqqBa5\nVl3EK8R5TBRHZ+8jANQ0mtFnSF670w5hrdhQXl/DrQ/GdrU8Xfld37tV4fQSofxj\nPxmCNpFP4Z4/la6oekG7RIhCGkiB5l5yDgrOIw5ZYmGfUnxUHY/05oNAZYw8UAWr\neLEbPwKBgQCLCiTqf4YxzXCm8+RrUBvz5Qxo+6mM/imEq2tBPgp5wmGJ2e4msmcf\nodBo/+wVUoH/z35h4uXXUlKLBP/egybIBqqfYemSYRRxImZ74pohtiLAfPewlLYX\nY935GfryUyz+vSlHED0JD8hgKUXMHcv9WAR1LGVbJ40u3tdGqVdC0A==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAvgnFpCcp39Rndr6/LPOiC0Lkv+vdZyD7eieTwHhHeNcVMu44\ngQ02osRlROZiJrlYE6QVgquxFmH8AZjGGmnGujk/AVU9144UQm14dJsoyzE5mFoR\n3MEYnY2wzRtMEKsUZBnxMxVFyJKCA4KKqq2U/CQRlpl+lFqCV2hhn3tF4JmeTzJQ\nXwXIEa7EDGMYDgJZNiWSBjLHcUeZzApuClhxP163ePeYed90pOupAP2Bs88EjF19\n57KCkEm9afMI6ZLGnkG6dNiNgZdvhqvJ8QIxtM5HsDnIulGnOfNxoiUnN24JbxlZ\nvGXZzRLTLKJ4+iMVGoJBkRu+iuf+PDwwMqrp8wIDAQABAoIBAAS6u4d1Tg56+8Qx\nxRI6UfsTxBuepBjv8jI9D+lHVQgF2ko1MYTMrmJontiTHzg1fqKmf76LxVF4SMxF\nYBDCNyblS9MdqvdVWt24WboVLnO4GuXWwAENcS5im1bPaho0F8LR/nW1/tGaC9BI\njGjo/v8zapij40mP4xSHzX4plDidrxKZsI9GBLvwQpRs+3VZ1KvSSab7xn6BEoZI\nWFSPPkSXyf/27QIjmD9zyZvKtxDmPtrGp1CCSSoQDRnmDaiBInntnWcyP8AByzhH\n+eP+tQnMAEy1rvsMkOJ2hTF488HtUlBgtBXvIl7pjwBBQeopBDdP3/Uv57WMeLGL\npbCTyhkCgYEA8qi+ZBhvOs3vP/FKeaN+54Pszc6y0wVVsCRGyJCLIQYkggF3z38J\n6MUlWecGWWaC7hJpkazUwMxOBPUKhjjPFvx4rqjdsirBLLiJp7lfJC85XSYN6S/j\nsNmSxPxLCpGiHy/Y2Vttw5DM1Hs3+UYCRFP6iGftvorLnmPnJfOVuH8CgYEAyHxs\n1a3u6yIAusbgwnbeKbTlgP7tb+eKKIpGxJGR2l+EM07bxGr3EypTF3JwgkzHBnaT\nsVhHwWWd/YbG/aiikh0kB77ZlwWalLna2OwLvTyW8QU61TkyK+UZ8Jy5vgU3FIQn\neObSDhD2voauv9WSPN5fqAroe8UQ4NzUnFcFtI0CgYBQ6d2aiN9GD3cwrie0ScU+\nM6pZsgvE+6ln86C6riAGYgL4ar7lrKnEOUDMKpHea6VK907GaYgDxdxaIM9ilDzR\noFwpeVt7VnBVli8og1JrQWfyXIcU84gL53DeSHZJTuhtV8oKKEeAh58VVwUCFMsa\nt5n5tBX9jFB7PzrLV97bxwKBgHXJ5IeGMSCkUZlqzxJxlAUc6tnm7MkaV+mJlBpZ\nDEAobhtW8XdtAMyZyUe1TLTBNw1tDUjcfQv7D9+sF61gLmN6C3khf+VivXcz2ogn\n5p2/w07Hq9EqUXfImnzrF5W0qq+7FRXLRw5MmStHJdQ/PjPU0o6AGibdmRUDqUZR\nOmDhAoGBAI1QmpjQc3xE+ZCFnlzl2VvWdmp70PWUi+xDF+GTohaYGcyz7Uk55pLx\nVPp7DIdDhH1aOmdLT3yCnnKV3wf3cIfj88aj7bkOnQR+Px8asfpqd1gMbwSG9/9t\nB6nbzDb5rKCYU8IdhNHyn8E4G03A8WkaFmLL+5bAG6qzH+7uwqfb\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwADvj0uBEB5S4AefaOcvktR3PB+j/3JkWzfx86PF+8372syL\nUuHevCiN5a2rhmFFl2U3aCYhkhejsHRcikWNh1ub0XsHxIxnoOmCDOBr6pFcuuPZ\nnTn9d6zLM5soUY2/PuSUHJpgcUs0BzDaQkYOuLcs9S9LnudkgaGiBWaS5nWfN65A\nqRYIGejcR9YDKavMWKI3KjK4FcdRkXO5HcbQT4WG1bMhGips63/+hBcQLQ604cJI\nTD9hx1l1p8F1zmcXQiovlu+KLXTSE2jh6er7c2jtjdOsSQn57GLfU6v+kGRLkmAN\n3QD+AubzmyusT3DoW2mcQNPrN61vN6vzeY7LHQIDAQABAoIBAFqXlnCcBXdfd2bw\n8cwDWLUEvoWFwFFk3ekUSnrUiTO4Ct8/p3Uh0YAh+mGmQcoT10wJm8zuQOS3dqpp\nHMi47IAzPx/l1tJsFPEdfBkrQn/YulIL8791fxyaUP42dR2Vrei03RVp2ZtbCl9i\nM3kVqRiKhRHikOGW+BsZ8mJt9V36ylrzaN7NkG+vEFdTmrggsJk4IINM17MwaRz7\nHy0ubbwcfLDIiLEwDjtqbUvI6uU/frzzXTfEgPNvC13Tk1BGQLZWlykonbDHRjf2\nKrdQdxW+KoPtpfkVBhGigcLsxFs2/6FJ1T+M0D0ZsHOf5ZX/gNcK46ay1gdQ9xDr\np5ps/xkCgYEA/Z3AJe4UEh0cOVV9PRlrdGFvOyGBmIg6XfhDwxY54TwuDjN/BB5U\nMU+mOb5NcJPSG2R1nP5mFEt14f1G6wb51l9Gd1D9mB7K4DsfvZwv9VIeXErFxTz1\n9vOtahmbyDwMziXaaBVfKEIaVTfBf+pTrEz/qZ9gD2kf6MiK+uei/IsCgYEAwc7u\n+8QC8bBOd0fRi7uL7IXa5jcsyP0Q/1fIOaYk8YFM1aDRsrObBujje2+36uKvEE3x\ngCUm76sDMhEtyY0DtLg+EDJ5mLMsj5SpFq5NaHcM3veELPb1b7e2+Dyb3dhDVQVB\nbM8/jLeo9PmGWEHnP892oBOygd0ZUcPEKQR2g/cCgYAZ7oBQIIjC1WEmZ5xXm/rF\nupSx+hoTK5K0OwdKskIWyp127EMHe8ZmCFvZuaCXazXngR0ZgVWUq6aRBeGzdFIl\nXjaJSqmzXSqnRMRZEdhBgDUEgMy5/yKzLnVDzPyFu8YfafVsAQtqxV+F6R6K4r3N\nd04I3Pfoe3o8xgyX3QloQwKBgFTm+F6mwbtrRxSOC8gxAt08zmZn6WCrLFUP+ZD3\nrdt2j1hOuU6Vy+Y9A9rBWEq3v4Ve/9rpa6M2wZGfYPWYpvT2eYnS8t3oMC8+Ksza\n0uJetO7Ac9DW5zJnwuoAcy++W9DVGpI2QFq+4m9fGHF1fX7AvfDmcB7f4wWkwzxM\nzjaFAoGBANtVBCPCr3iaHS2s6ABY03UgSnji3Dk3vCfElI3wM1X/dDMBdbZMmm8k\ngA9T8WlcU7DXsANawjRKcejjCBcLvpQGyxpMJGdUXnWYtijhGcDtEePZqF+DLcZi\n+XPOKHRSaa2Lh6bcBoZnfK/RcAO/XdQkNGwraurwt5NkPO5f1G/J\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0Af8nbChpkDkUjBCdP01BN5FaLMi3f9BQ2kzwP81tm0V6VQV\n6x0H4iUr26oWjQ1/BfDSfbSbURkgHj26UJ5RE4FDVZbK24gf7y04P66NrMhvK9Xw\nNBqZAHxtsxsdaNv4HFk6Yzh8HBS8Is/ImsaWbz0DC2FrfXWX6CW4L/r0B0TfAD3/\nDdV/f/MME6yN+Ix5xfMp3CNl09EcphR4ahrjFY2L3KBax4KwA2xxQ1kkx1Bksqdp\n9b3mXqp3WWA2wONCfwU7TAIOcFpwxSrrlE0VCe+7WEJoBVlwoBeyjt97QF0hHv5i\nWN77FJEY80+bNlXPs17sT8xtE1jIo6PuI4rCvwIDAQABAoIBAQCY2zWAPNgaTBY4\nWeOLCL6bbCL+EpptGnC+8p8nkZ7LA46VGsbcMkGnpn56cpkNXbxXBiP0X++8AEU8\nU0kCkcGrHlD2o808z+H/IW/yQvUw3znosNNFxcFkvQjyENtkQDUnx1yCqW9wRsAS\nqDCToZXJ+L+y7gtIbU6IMqcLAk+qwljqXCTwoo5Yx3p4Qk1jfBV26Kb+3Tl24mlt\nrdBhDrOzLKABrj96t9kOV/pqzE+inewt3eHVJTfO/UCw3wEYwSzxIXo5eeLp2Oxh\nKlwdIGVKhHyGMeVJ7eJVmMWZ90FAdTcwZIsg+cPvvkAVQq9d+aoYHePokl/UJ/1n\nWAcMYDX5AoGBAPsthIkKkZSQ5O3P3Qo+oOZKO+ZHnwMFNDJ9oPWolXJrOk3OG2V/\nnFG4/KNTprNTVDCxBpFx22beuwO1s2vqhbRySFxeGsB8EJiNwGw4CwA7/x4ES0+0\nDLn24UZhJapYDYRO4O6KgI16CfWGJ4waaiH/z1tymx+jqEHc+UOoWFpjAoGBANQG\nack3ncQut5imVhm6dGNO5mRuaj1DhB+s5ZskUl/RrdQ0dEUTookFZpvWhAvzro0N\nox9n9seLF/qChQhrzofFDWdOICSUdyXTo2UbR/719hA7ugEdCnOQJtAxa90rBsGh\nJexuuJBwERE3PxiQIcETHnV/0AfMCEcHMoNflVb1AoGBAI3xzzBS1KqMp5LtIREl\nC3CZCvi8CNSAIFtYO5oYRzx99Fj73OejEgfs7niuUUqOEMnjgL65BTBBNpaGHHsj\n8aNj4hiw5ZVZh1du5AtEMJNVVHr2GklUisydA/nt9Icrc5gNq3eitbs9+FrUEY1P\ncCL+mYQoZBmhNvjhk8v0WtnZAoGAX3r33pZ+JJ/JLCQT+Agga6xXUnjqvOYPtNf+\nmiv0MJRTTJHGnmjmNABLofgrhNpbC8hNYlJGLrwzsytdz1oFKt0RWSk0jmSRd2Zk\nWZ68gFOxWMQc0m2KjFopMG5hHbtHkqEIW/3MfbcJ6I/RiTU3DiSW/eDuBOTcAWh7\nVfAQZ5UCgYA4/wR4niMgdBs6wfIECpLALeGi5GpxGL+L1U4Gqp98YVF/K0S98s5T\nXIG7lPd1yw6+67MRkm8oXvdL4GqeDZb0iklwKpIK3WA657RFr1zvtD/j8mSwHY1n\nocdbrFZZFvTAyEQprvpEVSbIwPAnheCGx1EZ56r7hOb6eo+kp4Paag==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtYuiocBbw6Nk6YjjJYmgbXOKRglYtIVmh1aFOe5td7uZV5AO\nnJzVQMSLN7H+eLfh6Ld07nSc5aPdt4d3txQIccTsheLjwR40FCHhrNMgD7gRJGmj\nfZg1Kt9WfTD3MWRzzwyepjt9k7HEtng5UjtNtrJVF5UnvdbBKHuCu+vq7H5INbNL\neDDEZjI5LdZfWeuBZGjblPSOXzRPOwPlphswbOroxjb47gsPtdLnpAq874B+sZva\n5kBKP2/RWmSEslzS8Dh8y9HN3Tc/ducIJeXT2eMhG4hBPS0y/9P+TUCFGw/Wq063\nOGjpZ8e10TjbhS4vdupKzv8IXpOHmPeV60n2jQIDAQABAoIBAAMuGv8yW2kl55Kf\nrzrtGj5Qk89+QmuHGrCsN1E4d/ps8tax00+y/5lO2BF1sLt0nSRycj3rFQSAxlV8\nrgAq/arghDQkMVEkskzX+xgH6rvbwZEduJtumx/RIba3sNzQoaay1eSbjiuUJZn8\nqSRjnYzhutPa7QNKS7/lyfh4vXi/Bf6W6CNWaCCMN+i9vMC3uYNSerxUK3tSlGLN\nFGFKFFVaID+muq7MdLAjrBn1buxkI1EBuhDjgfXV8Q1uuVhqyuL0mFjMB9dQf/Wm\n4pjJMgK10MZQLB1SJsM+ETzLuz9823N+10LtFY3LVI4DQCzHT67CJ0SqDS3uzJDp\nwVZkJwECgYEA3Xc7Eu7o8GSq3UFP8NTAzkd8ZxuRytDjd+UV/5GtzYr8KIy8hGxK\nH9B/zw0v83CWLI140NhZKFq5A76fzo0TBgdOKBND4Bu/+V36JYg4/JXgu7GJTzha\nyc6osz9xryD0f78xclg6C1NN/ITkL3JeNdcl/y68muwHqiQDTufpNV0CgYEA0drP\n61MxpdsYpqMD3lzrw2Gbt6ONnxkBhPcNTQWbaFx6b4ypNbokD9/kb6PkZvhvSsAA\ndWbAJEUSCJ5+hGLN+DozoR8Mcs7FCdq+ckyM/20UWcoeg/i4hlEYdFa8GCh3c2Mh\nHTv7BnguRe63Md1oZuB46qnG7BzrpG8Vq8ZkAvECgYA3abPgQuMJ80ZFYigsbC9p\n7b0U13MdOjMQNsPQVJUWTuZVBxHMWIQbfcHc/sG2ub75mtRV/SwhysFsTHAT8HyI\nD5/gqyFam9bnADqs1W7c1GuMyxk+Ny2QPSM85mz9ktNae25V2/l6B/gFbQLjVEx6\nUEDznrAWW2R3j9c8EQ7rCQKBgBagOu6qZ530Q3Ij/VTKO/ky2vEvxyMyNQG7CUoW\noqzb26+PQOc0DJNRlnWa/BNGEenDTclarLi8YbdgL0/iv0ZWMB7dFCCEvqXTWdlE\n+6bGgf00V5nvBSYmNDbG6zzHkCl2tKlMQEJwTrznggT0VD9TUHYsm+/1JkryVynQ\nGBRhAoGBANRT9+WAW4HHiM9jvwBXH+3fZwfTrEuBMieb1bPqI4FUuTGuVNuXNFlz\n3NHZIqzmhR8o7xoOzLYnVO70Pg1peVyivyb99E9jxhEex4M6fPiL2caicxqLNqSk\nShze2Lzn2+wM+0sOfa1V3braLqAnEOcDOUbvPZNoKAxhqamILE4D\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEAt/nyTsjZecuS2w0tgTAjIMEvl9GQXlJzBI0ja5zpf7DaOnWE\nYNKsVcUqjiUXZD4NDx+cHQT+Nia3S+vtpHMlhkWhNDPz3QbRisidAGnycn8AKAh7\nizEc89M/RsbGpymJvIUeuAGO4fJu4wioTX1sD2cbHb+IvgsObyCo/yzqBLOV5C2z\ncs/XCcrJcUFsEpEWKiW0LF1r3zkC1/fYvSqsistAdRYev9OHRyENy4rjRY8drRP8\n5RTy6j0UMFpHzrHhU64fC5ov4RVYcR29id2xAQjjW5xaUJZQcG9aVM4MerYavNHj\nI+Potp2+qS+CI8xGLunkot1L/1VWeFPc8Vr+UwIDAQABAoIBACgmFaj3z+3ybSP8\npjeRJ7lYL2i0RQswdwVBP7+WSKajE5y6VJ9EmnNFSdonYglwOS1Zupniow9yN4IM\nBJpkvoWk4F8iD0/m0we0a/7RICW4bXJPF5byqtzS9vYdHvEP8Y0AiAmj3WiVnXlS\nRFhUzKY33AUL7W17wDSkLf0x8ggAQa/X5Nw7hz7TXkJ5EkWsC+A0OX9hnCzRc37R\nblEQC++H1VIVM1SINYsAXSGpLt3Mpx1D7uv3gcWtHOJXKATmF+O6I1vu2GJ+5DbR\nehqrkhA+J2hf1YKZkHUg5uqIHAggPEbM06TmaGyGj3dXiskgLOV0EepAo+s9vMDo\nLK2Y9cECgYEA8S8P+Y0qWL7Il7ENycxvzV2r/ETooqpEfpPubZ0s/ipNavM7eJX5\nI8RAbAtH38BbdVJ3LsIQwooMefubhVvm1dFjn+ooSII6pQkpDYcA0H0dpqIoz8LB\nJp/YdG93FZnTbTQT1VM2mrKZ3nipr+FPGXgMvOEbh3JjbaYjMdicTJECgYEAw0c4\nWSEsbgipZJamm4fHr+VapmfGxigApaAz/8pH+3719fqGGhYHULsVeBSQL/cfl/7E\n00ss5baX33y8kcKx5lpEWwILnqyWWPJcasXd5u08eMk3b7uQlsZvFqocB2CvDFkA\nmMG2REL34haxh6RrGH6R/QHBg5zq5JXQ6bapXqMCgYBKBordqnFL6crBnEtCG+hY\n3R5TgIbynlYFj7wHks1jeXJnmB4xxb5BNxQa1V+LEow/tz7zcAca3qozqoEybmGs\nUOdGBuW0U5xSWTxrdXtPgodeRLlIA1urUjZqjme7jooxI2HPAQpqctT9ToWugPX5\nODqYonMDoJ1Z4zs8KmLWsQJ/QBMeaiB4c6Jj8p18DYx/+TdIh1GF5384tx/Z8BRv\nihYenpeVaPnRbKv2X3Bf3103AKiX7Ly9/qHrM2m6dhlsRilYRIRnS6lVq9naeS5z\noVI2hWuAxSjGSLLTwJwQllsjFOofSABHzh4rOAQ6PEe23FKrlFj/JjBW4qpF4moN\nKQKBgQCdhD3H2mnygTDvJM1+xsbbkYzFoZBxPgWI5riIExzLcP56MKpyyAwrHRwG\neBg+RxYEtyYGdx5UxcWws8khsSE4pF7B9j/kVFkt5Tk778TopArlYksjL8JGvGRa\naQtf3HfP7++J3bYUQ/hF25z0i+2+N11nvnReArbBY0D1+PDn5g==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAtnvimJ//bDfQtmQZ+6AhGOLA0ZK9BKbStH7Gaho0IHv+hOvo\nwW/93Ar+YFW2/IZaIb/xOfrsQspXsz4/5iZat0pfu7H1kYWSPmoYSE2e3Qglozs9\ndZq+7g1u0l3ive1FYO+gN/27zzCX8VvInCkzZz4jM3821HWLoc+e5sVjY7A/oMKi\nEMmya6pnyfW4v1uX5Snyu8ciDx/B9spKikaJoMpOSZ38I9O028aERb2fEIbi8Ed7\ndb8lmQIs22vWK2cNzUZjvc4cxVZjWFvIslhC9q/OR7KpKnGLgvRy/+/nwXAS+rit\nsv6pFOfC7BK/KVplkXSC03cfFL9fQRFsfCJwZQIDAQABAoIBAHPa41oS+o8yQIgL\ntZNTbq/mBpUHnIm/gDan5kCmskGNd2HchMGK4Jk7QZTX1n0srzyOKBCvhiBBZryd\n4OO1phKIXY9ExKvHKboGkbQV065w5bRkf/iSbnWkgZ99i98ZLNsGR6pEpWA9cboC\nk7LRVZlOkfq34Y+xsRziO/g3iYpX4LXEbwNoUWNj9oummFHeUPijqQ/AgJ949SQF\nH+dEAvtQLW8guVp+lUPMyIQepFuMPXFhpKUVPwzV2hQhwSVfU3vei3ghGyPLBoP8\n3ZKTu/V5eNwZKmAetS9stKwmAj5JjuJFnuspSijtrK6gXY78v/TAMPIL768348Uy\nR0fiLQECgYEA5BA92LpcqyLdMQyFx2hXoLfGvah/elbrSj9TaH1EcI1ZPzyQaXqE\ntCO9hebVH2UU48DIQVmAMRflKpDa7fLSkhb1izfVvM5TYQ6ZB4UMw4giRPMblQ1R\nHZWupemv6ByDrLhk5xeyoWmpM8n8HTNJYt9ufecKN06WeziqEShqLE0CgYEAzNZV\nAyxxuWS59NXFK2sOlHIfBn2vzhzBHR5r4ezkTiQCgM5CCjM6Imu6wSJF0cHi9CAY\nqXTuWrlkb4zkjQTxp2apyvJHNRAgO3Csi9MQcPQOjkR7eY0MnBugo3DAYK6lV17l\nqRGsRiqUtgpuXlXJpWlr0rQ73svfPL9fD12BgHkCgYAJ6SrG4FJmnX0/RGwL3lzw\nfwwD8e6YQm63K0lUoRz4Ryw5s0gTTV+29FaoGv/n/VyLCTWqoVkQyRkMYH8jGApO\njvDcu/Bt5Al7jkbYLNABL0lN9cKdF5cO0hJk42Lhk1ulhd3crQr2OMFBnuV132Sx\nB0Sl9ARdouynLi81z6vNgQKBgQDEKSmGfYR3H5/5eNJ2TvLKdDOD+s9mmiDRqvVz\niXHl5xRCl8OkkwREPFi/Zfvo8T1PvoD/nww387zcGtgwuCBXyLgnszc/+K51XeTP\njpP7J63cznZUpCAES2zduDdzHy0V67oR2+vX0iVR49FoI82Uy81HHfUL2r9xJG97\nfndrOQKBgHlmBquvwdMvbea3ViIGV3regbcI4jTtnlzRUjjA3BaoLHWh4wNbf/90\n1JvSQFrT1m2yTahYdHlWILeueUvgBEib3GrzdiGW5v/SmNjA3yd3PkmTaU9pdH7W\n8ZSAkffKnoLMMngKg+4LZzC9At5eU84FXsLEZT9gaLPJnxfsg6iO\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAnwF7h/2+oAJ3ABl/L96Aq9zQFOlj5db7LMuwcIi+y22OVvMC\nNK7sMPdBz+cHgFIEoRZRFQe628bozqiMqLeqTgiWvV+9ZA3jiMp+GiAfZUQ6yCzC\n3cjGm48BjjbFUlqxHR3E8J+pUU3m6zacLi0IHK9n7YO7Z3eDJEmXjAYNyotF9Cv/\nlCJ/BJKB1vUXf0Etw06GKmDEDnSwBDxI5NgJ9kg1AAnaqjEvi19tTEhSudlD6B4i\naIC+/+4RO4NH/exuHZx72OMqi+8qs6XBeDwLDjHmQIDhyyVWs0V+ZbPZGo74sGVE\niXOQD+fNG2g7pHo1Qa39jqhQ4PKrLeIxOYJBSwIDAQABAoIBAQCZ+K+MgnRjMb/W\nQtpcmlNzGQIOHer0l/bl6+BROLm497/MBsluBq0ExYnfLtKeuc1tx9SATtntFHJA\nHAsJHcE2iy6c9ubIyoI45gyyc8SzJRze1ilk5pUb8aEJ+5OcKeI0Va1tbM5iwtMH\nlfwq4sRmMpyq7NM+YAaHzxOHLh2QsUIeQAeJd6pXPifTHQn2DTelW35ZV1MGoBBX\nMb5bkktCtTUWXXruZBKI1n/9PZ5as9ABHPDex9aWrfAyighOjC2ztfalsN2agFty\nXY/8D+EDJyCkmcs6UXAukhaTSS9S0GUQxPtxNW0BGsGA1+juphqkrFYNJsSvtm7T\ns7Lo3+AxAoGBAM2c6ZGt2MZvpx5thvbh/ZZTY+04so8+wIAvpvXY7ATkS9RFcUEU\niuJ4/BGVG57tCxxdtxiq2x4MPa96FG9Ta0LQN1WMISMureSr2oUfhIHVgxB1sQuV\nCTMrkqIr+XREqb93EGf/IuqKLsqS+tB06KmxqSiEsnBvOZBfG8ceOM6NAoGBAMX4\nrgnqxuLOFgfQEMUzlkODbYgBn7LpXPpV7y+Ctbx0sZkZuLJSLrVBiEPkbbj4Xu/L\n3GikyYRVUMhgWAsUWjd6I/4RJ6NC5cbQa51QsKRK+N3Dq4pTMyVtDLDY7F0O4zs0\nNU6w7JKNglTyg3gNPmzelFDAv++aCqC7pOX2IaU3AoGAGLm62LroK4y3sxAGv9+T\nSCWFCbzctB8etQuF72CDmV7+w2sKYIIg5XUZEuA0bxsgT+vxHbVGHknojRaIKI8G\nggVZ8h6kyX9OiAYmhAWzQI21ciBFBxiH/7BItNnG6LVtGEowHeFviHbPg6rDIJg+\ndlGX/vheIOkvKe5V/87hk+0CgYEAxd8pFQB0iX0yx9xO5swJBLH4aFg3JrvZEt2R\nlAsxWovIdx4eXR2HUeOOL03B3X9iZIt+YZlTUdF0TNL/LRPKmGicUPUUiTn0MCNh\nbHuaEpxc4ksQydEe7elxJdGrbdfThvlHzr9HtbN68Szz+qpCGs7dr1j4fqOYu87R\noofAWUECgYBQYLyEb9ojFkWV6YRIoS77muHH6iyZ8IcO5Hmzg6cWrCgFyEFWkz8P\n1YRaKCF84B5egqd60K+yO/tSBtQPBi09icxtur00qubGJV5EKhXu2Jgj1xsZc5Tw\n/ES45hiXWyNWN7gAH6HlnO+K17Hhz0QNfkbH5N4GbHu3bVePlBkLWA==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAqMdeWunHy7yckre4dmeQwlOpOkssq9uAbuxQvp5elmSP35Oy\nsMC5fcbJdWtzsdYL5bsq9dzA1QmV0/XPgrlDk99a1E+gYCBBAbCPKljVBaFxFCmR\n1nlaszu+KTxoh0iUt7Mp2f/rBv0dzz58FJX6o8AlRqTTLbVfa4YYtC97B3b5+G1X\nYkF5MDyBborah24dZG05QNVasi26uHo7YNaWVf4ecQkzrQWyc9Ir8SYG2RX8SDak\noiWQlpw6agrYm0/GGu8Y6TQPhQa8wOtNO2x+qFQBAwxcJFhhzfExiF4AFR6agZkL\nGjPFpmy0+06ElJx+KwBNXM/CpqU2xu5yjh4YrwIDAQABAoIBAQCKe5MKy1j47RgX\nNhFvbYiUOerXfsj3ddIKsXGLBgycH4UBMIHkjy1jaxDvfCvglb0LUShttSSvZ/78\nfGs4WmR9Vz2CiJcRaEWOalz9pQecaBuNLJKRxN7Lw1BDK2Kk5cPIeoPzcA1anaKw\n4hcTGQAfDdBIB4OStIfHhK6OYYrz0rElkaGQuyFl+YFYLF2XXil2eOoT6N67VZsn\nHifkkLcBjqjjlwqIJJmUfjQuX2/P3elmb1VIwI6pwm5aFNy1kLvbBe5DyjgQV0Wz\n6xxItslKe3oWuwvuw3zgP26WKrnW2GehNRsX2P0t/X0FvED5Rsp/HyJT54q/dpd7\nRvoQERUpAoGBANHwHc6ehyiymhULP6KHchzy172Ceq3qmAvJP9tw1QREGwaaavDv\ndqbsrMQbpDzMHzZczrFaN5VZlAkTSYmsuAkPCDrIR9ZbL40YqkZDTmnZSElihpXO\nwOim3Xgcvys4QDK50QDtILoBDk7P4NVbyUMOCoo7ubmqnZPZxT6SUJ3lAoGBAM3P\naA0trgfzXkzdGpWB1cm4dwkl960//fhXg//wMZR/HpEsq035l1vO3PZ33CqhbyNS\n6mar9WRUncejYOAg0QqphtFwEWaVR5eULcYV8rOGuLTBXJE4MmFfuoIxW1pAaQMx\nOU0o/AlhRIsbdaaM1Gmj8Et0B06WMOEM08nsX1MDAoGBAMFDt2z665o/sTEmRdKt\nthOIS6uebqmkiYxwHGU7nh5rRCX0Cfb7u9M3lmSCvdNqZmF5Vp5WW00WRf2Ez/H6\nGwHb4MucjOV81G2kAzzjwVAYQz+rgBJxX43N75wfeq2i/xazkQC1S6dwSWesX2KL\nOOCOEI3pJbUSqDXwCHDt6WExAoGAb5PEK3DaasEMHMu5/9f12XjCQBlvX2PUAnEf\nxP+pQYJxRe9bp9hqsIFD1fNBT3NZ12K6ZZNQZGZja7otaRUhf5BM4PrK1DyWJZ1V\n+5AmliGxvX0e/DPlTFkjujWb+x/0TqC/Gg/gJA2xpWDrtbGf7u3L9nwfAgH22euk\nXlJhk5UCgYEAtTQrgThfgNzEvzDtTCRDqb4X9Q1A/3/ilC0vI5Kp7Q+fIOqaOVlL\ntbvPb4dRYJ+/+YRiDLg/a5XwO1R6EPZL1d8OThiBUHckhJAwdj9+JqDNE+v+QZso\n9X6uuXKFwxaJLQkikK+d1Foo3VOGRuK20B6DRzfVuYggH+7bQ/oExt4=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA2SXDugpGbqkdBddU8f/0r+UTz9Z0sM/CjGhcqfNES/1KTSlk\nvKiY9TUKZeXNXQifWCLtIXgETSrOgDMZW3q9pon6gKT1MqaxNGFVWr0Fr0tL3+Cp\nPh0vPq8MZTLG8uBcCcCiQebJll6IYkooSyMvz7O3HhF/xGMa5CQpRvRI3jBFl/hs\njU5Kzl5Bsrda1ZRCWhTREZnF62a+scbD2y+PoGypJw/AkncLjWa4kwvAXMtRTqOD\n0r0E2MAMsvc4Tmrs/nbZcQuQIXy/B8TYTG25NUhdguphxRT/JVBHrwZYqZUs3eW9\nlUp7J6FG4/AW6Pm6Q7h33IeBOsDy7TsDXuaJcQIDAQABAoIBAFwU3pXyq4Fbp1oF\nwRjKEGnSe39EEsGzqG1ACCNtbEXJCAwGhJhX6lz4/ArP0zEjaebHEMWDjldm0qCc\niqE3QFrx4c9RpESxWS28JKTQyStXg7pRG5OibplnQ5taV/FAYOclz+igRFXGipEb\nn1CVgD3UmM9gXkzaNdoO144mIwxMpey1x8N8HpTT7T15X4vUJWHIt10uLe/foWgN\nPLX0xDeJHazniqYCiqh1dEJ1XWJ41DN7JeYPOVv1OlThQ0jp8KV8kVWjyYfGN4s4\nnBzIsTzpmJRjxGDlBwM4HGnHTzOznXDyb9JMCbxwTYNvvwq0jiSID81xvNDxOoZm\nLuMSFRECgYEA9KN30v2EyWUhPHik8uqmKle2Ojf6j+oPRO4rgKBDvrxxyDEixuAs\n4PZpU1F1Nzt8T1HejqI7cHErJ1EiExLTLe08qGGTb8yM1l6WtixOweyrKr2kkO1W\n7tuteqn5aW2953rDmBn/EOJAj/77GyQuFuyRuZ5vybzTVPjOy2COe4UCgYEA4ztz\n8qwh4ZJV/hMvG/A9IKQkmcx7LI27D1FNhyn+udALAZ/1/QtdQYIf3jScwURnJSxC\n4IaiNm0Q+6ekVz2zPOTfFMm+0umF1+ahJX5IrQqq339q9+cyHyJuH34J1u70B0Sk\n4TnbSCWUNTJ9HhZI/blYZAKXAB6F6IkSwLzUy/0CgYBij8mi9AkfquLtHh420FsK\nDDSa2BrXCJIGT36vX+JrYjjlUwaT4ZWzPRPKQbNcAVdgDbgA91PDRZUBTirQTHFQ\nu5gIrudbz6meZJWL5+YDM3ryyV0YFsdDjLYkCX82/sPYExnimfatsroYSb6ZfMYk\n7kL36eFYAf+S6HtIbSp6kQKBgQCn34dkk/if7n1rLH8UM+LlF38WxLOHEdpNkbIm\nFkdivSk0dRnFbTzjJaAniFT6j29oTg/GUpDKhr8SPCK0dRfS9/87G2TH6s//1hnd\nWjOU65mbbkX3ia1ExSn4SCQ6zuQn7nWlExpGbcTVnExFCw+3qtN7Z1JpOyOQzJu3\nGopXfQKBgENP/rVDqTXa8eH56wKCV4o9HyLbziRUXTkGV93tHoiCJenbN1KadXbD\nHox9FACV7D3bgt0sn/ELTnU1+VwW720Tc4kLPHNe1iRSjsNmio9BQChwNLl/meb+\nnqL1KIa5cBFIXqleK7wy4ggVxdGlTpfFj9vOxmYceCLgdTESAjXt\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEApWxticK9Kpk/0HVHyKZHGmT/LiI6TfsQPCTbHStiDvDxN14a\nPVx4505rHvj4PUJr8Lg0yxVDhuavhpCc0nNlJ+2O9qC4zMYD+ats0Ixu/KQFeIIv\ngMyOuBqVXRv4yU9ik1UDG3fGhx0A9pQ+DBuTZ4CYfl+W2mXvL6Z/36kD+ld86Ou5\ngdFmAA5ivWXFdTryLAVxZ2gYvuXL8bwmkrOKYx5LQTC7bJ4wKcc65CuKbNasC4CD\nnCco1RoFqC4MEgmsTUU5VfAE8+1imzSC7a7ljyTNFDMHFUomXgMTZo85+/fOm49Y\nI8Wcp0ImaDpTXg7P75QaavIOZjQC8TnJn6c/4wIDAQABAoIBAEd1PSQjQT/jj1Nn\nNikQ2uol8q1lD7ab23l/mj9RfDxkDG8UcVVMWtXgZBmvBty/ko0bpYKMnuzpPa1t\nTrECNBI5ZmZXKz0Z1KHHUWwNHex4lfxmkEqdEpGouhjMen3LkxN9x6fX56R5/O6H\nPv5P8y7kt/rvI+UX32jADcD568P50RBJbiJWLHfvs5/O0S8Ujl9QVSMq8/zFcHr7\nEeuFFfQLgj9knZB0hLtFaNe/SOKaQYPN29DFdr7pLNGPK6vJCeZJWUVPeylN2obB\n99BMG0JDFFDbgTCsa+Uu841Mo5wmxyxRzbbue/z0H47fugaW9BMeIeWCscHDdxj3\n1I/LljECgYEA1PikHiX04VYV2yYm046aMAe6BTdPRv7mVzTsYgpn2vAXjim896Ch\nQnZtl0L6uXJbyShekDLElHQHfE0CJkGVMqiKKxdGu6BhWtYTXqwZCnzueSxAlpYN\nYyuv1pVavb19wseHLBoSDL6qENLndi8k8DbzC+Y9eDFUlx0S04flDF0CgYEAxtiC\ny/4oYSlBQV6LPRVKdZrt038l1JeHGpiAAKlK5PQ73wMGoN00Ys5VGUPnlR3TqraT\nXkqJqqzZ7nKFGi5icmEUZuTS6jnNW00lWbJG2CNRaiI4VuLo8wAFkmU7o5KkoCMq\nR+tHo0Pe2SHx3QkkaR2wXa6II6YhjVGxdtQiuT8CgYBPWXy8huPgeVSXXHWm6UDl\nmFtkyiDNkPd9kohoVvwvBLmiDEcN9FlhDAL1Es2b/tHX9ySUkXXQ4hJP69lbK6BN\nSaKBP6DpE25yi8SXx4Nk9vtbRnOiJ7JBOuu/BdLTUyDiS7k/Y09KVFfFAO+ghxw6\nLB8OVfCdMbQUwY5cJgvfOQKBgQCyC0EqWJtdTCxC1N3Fxt+R6DRU8H82GKt/M9dB\nQFRwJwPIzpPLy+VRJ0DpS3T7fuZ879eKUSFtMjI8oEvvwmKh2a0D6JW15iWrP6tW\n4hsvAoiuiI97tNcyZe2XRI+fCfnf7Ty5gsD1Phs2vn8rFDmG6W92ooUBropSWDiX\nlqC0zwKBgCQDSyeyZ51hk8VRMBceQHqVyfLsdMFrskcdUBrat40B2d3jY89J9m9w\n9bEk+wUu/hDC05HJ/2UuddSxnDlI7zMm1q9KG6lwZfd/BOrU/kZ+oHTOsstzgokw\nsgbkgHwhyhObf1J8hKEN349jayyZNewV0bL4xKAJDZ506oa4V1kB\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAvWN02cA+DFdVmQDzqAPcmmwU/IMzY4c1nP7DALSLA8dfCuKU\nqj92FbG59l8KhwxcNbwvDwTdnRKNlOuLk7ROliC3F6CO/54fQ7/Wah62DX8sCGel\n56O20mNq8uvRIYOPoCVKxstOZsNfN99cEre4+npXkZ1VppaR7pSwzEXTdnBvEr2B\nnQgbbVU8GVacz8S0Y4qHNStMxuENdDGqxiUNkFUU8XMJk7rG2lJ/wt2dsk+SQ6bJ\nLCLEWiLCVqz6ajdqDCJyXTBQSmuTq89pWgnSWocxgKdb5DRCE0Qd5vknIacDdGQY\n+tBoKHM3PPZjEFbMJQi5rcglZNGcfOdsphIsfQIDAQABAoIBAFoZ1f6+igZ/Cjcn\nj8QQSkShRP65QJqKOgR947ITMsvSbzITh9zTIk/B5RIEN1L2m2vpIo5Hp1NR/0+p\nHBdiQb0ExXaVKVMNHBy0SMLl54DWTtGU1gY1CgZfzALUFAOYvpzXARNAqAUVz9ga\nCIjTJUrtJ3ocWyJNIhJaZR5Z3ffLRcZgH5OhpmEa6pR3dFgJEiLivFPJU029nBXc\nHuhpvo8KzBdnLw24ygfxYir620KayrqHaQBRqxXDNUPTLseNXx95YStUht++ZWs3\nwRFjfbEwBN2quhJYnytAdvdP7st9hsHIGL0AVOWf7Ojb6jAW3LJeUZBeZYVBfAD/\n664FmjUCgYEA7R5JKBqi/VXgKbw9xuDkx3QSC/8IrcY6UARxlqoEkxyM0D5qhzEd\nb8gq3JF6CAktWnGOT7aax0t1SV1hWJFjpHP8rKDwLvunukVa9Mt+QUUCQ1hCFtX8\nuqqz0oF0cFFFvLY+uhCfTSPjFPwkTtE10/JXiixaNtHMXHIUwT8NCYcCgYEAzHgv\n47zcd7A0tgPpoD9xROWDHMKQnlZBnnZK4a8RTiY60GkDDb7Ua1ISRb6WYcgvmUMZ\nAxJoHIZ14MOxTtj/mEO6zHhw8vCcelDX0Xvcgbdi8BCz9nqd0j3MH69TJ1qcp1z7\nd6+AYVeqHbscjrutAQXHnAMPP2DSDfpaXIgGStsCgYA78cQc118NHICnDGext3ke\n00Ect0TCabKkN/tfU2wEpNsUajMUKl5+u+PaDw0Cqw9EJiEMFeXbLEBiY7EQavD9\nyjS+j5Eoz4X/mTj9WdR5O7DuxYkvrLlPl4gOV5vQKTuwEYKaF0mufrwRugyTwGAT\nTNjEnaHQ6gj39f85O29YUQKBgQCBqdX0vdexrvQ+WMzW6mSSJFm460l6n4iNTMa/\nx51ZybSSZYWiOdnLFQixYeNLrv9AWeSKpimPrVUja/YM4l9z17+l2tsFtMAGcS3r\nkniLahg71NvK+ocKxFWl4jEBS9z75WMyywDGuikPIZFCkpFrjJbAwM/yiRur8Y9M\nDpau8QKBgCyTClWT/3erDbluh3H0h6GYqJjB62jeFV18wkie9Mltvm7HaHJae0fL\neiZJseRzNbZK90deOzr6a1ORgDn2nGdsV+7qbjbxC40utdzqwjiN+zIZK/+wrUEc\nXgo+/0N4mw8PZQPSWP5GgiIl1xvmIQnoUQLi6FFKW+o7D0KkQY4C\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAxjd7gLEQf8QgoRKo6MkzEr4I2yK/zHSsIWLgFSmOqocLU0vg\nocymPMFTFAAPIv6zj9RqeWgEmOhiSPr4mGY78sNO2I/YSH57L+5DfhobzmyiM6cg\n1WuwlGYhNqyYKNVEji8MyeAqBWrwU+K+t4EV2E5+m9EEipFOloN5wqCd3JRJ3ARE\ni0XQ9dFmwYEsMWBVPHOcCVxZPAkWPyeR2FXwGCDltikpixWrbUEzHJ2wAlcAHqaO\n6YHRoA7wAbBM5oqCtQCNuenS+Bc/XL45Q9eMt3dkU0MsDBpDxa0rxW4j+Pb4Sr8Y\nlQj1jjF171IYTmTx+7KTb4b+GsAE90ozfHNfwwIDAQABAoIBABH+JVLYP9QpPIEC\njZyb7uvrniguLlTj3mAldovVOuMV4RxNjfWB+zEWp3DwVG9ryWbr9EJTA9/RcGKJ\njZlf5l8yBW05GXvzJ0GBklTXD+EcEVk4+Y0F+oBuCOPKpQFH4rI5aSLGG2pke65R\nrdhAengpH+sK/SuK5yRYiOv24n2MO3a8sP4HzfZksv30HGLP+JLGQu6CNz3NhQX8\nLAReKMeOP7zFijH2n5YS6p4TtNZppqeY0FnBDN0h0WtglpftBWVHnfQDwRvpGaM5\nejxBh5Bf3JSPFfu7EmzMf2Hg9VQ5GFRQ5luCleKYKZYCeqgk8Imhr9YknAHRYaut\nqjXznkkCgYEA7gqkgUrfGNa6VT7qcdvjj1ngs6HCfZDK18hsBajQ34uoKSs9om/M\n/5R5UjD6I0OZXkXrf1RXte/QyBRWyox8PjXAKGYmi4JwCaTTg3mC1PlUb4NhS3M4\ntiG2rsg5DEPuaBEvN8Rs87ZIEyVkCEfJBte4fHcX638IwG83+Mp7DS0CgYEA1Sux\nRx1ss3cmJ/zs80TnkBr/GqIpbNEJMUYvAOjPVRGR2tYr/eXCYut6bviI35PQ/nEW\notu4btG8WKyr6uYDieU85nC8eYVO1zSIqZSi9V6LDcC0zI1NjtnXwVn4fnX5AUDe\nIA4P62pvYZOTwHmb7qUFhCassE3EdSfWEx8xlq8CgYBYsah38FDOLXwCNU4fnIHe\nsOtzowsXxDKAUlJFjZA+Kwg+RrlRY/zrIgROZqddKlvj74N+zwtbY/7T/hgUomHM\nwDRYGS+1faWHQl35N/ZSqpAWeMpw1X7FBizOVRZEhq83IWgJjGvYJwwV7jGmkDDM\nFubLoYhNG+lD4mzli3RsBQKBgGnG7IjlSPMkxIJDNzDfmCghvURyAyxD7dPNh7Vj\n0L3dogp9DAdI41RClNZL7V9D8gjQBPN7I+GZoWQ/Cm/dAxSB895FQsDqLV0MbGP5\npYMsDsQcQ5rc4wdngXAoPO5Di/PbRF2TnMKsZeh9Wmu/7GhW6o3QwxAZBss7oTaL\n3AdxAoGBAKITiKOjwpQQKETgs+uzmjblFARh7+3dyDNN0Sx9VKHG6Rw+2TwGIBbB\nUEAkHrbWfnRhQp16+J4cWI0bjgZUkT/e99r6pGHyP68ZSZf9fMb/DC5dA23UOm31\nhhPkrwEhnEUdjG23GA/UMvYcLLLbCY6d0yrb9aGA6scBpW7N9fS/\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA6AN8/0BoXce40J+ag/kC5v6luQ6f3ER0WT7vYdy2S0n+Gmdz\nOHsctPokOeS7dBvkZv2Fb42SPgRwtCEBzwfy6xEsqp03u/ONm7wXYOTjrnmSlzro\n2HTFnesVIouuURN4Icq9aqYkk4/mIxJS4XXaVSbMlaB1MQpuJxRjcATX5DYhOp7/\n4xYhwfxtxeKAuSbGTzzC1SXxuYisx/7e1n5gQYLeJSgfCdoQASf0YxCiTh3CGTKz\nh8LPrDwrzV55HxnqcFYxWQ4TlQW3+f/ZC0nO0G+ukESza2DJcveDJF4VJRy4dnGE\nMxTx8Ad/L5XuPDZnNrSuHLUCotwO7fJjDVJh4wIDAQABAoIBAHv94T9IXH5BzjMw\nGJOO8E4iqM5NUoGU09keGTBjk8iIspPJ1XnJ0X2mcNW/t65FebrQog8Lyav7E4Aw\nY5cQ2HFnrEdB3hdEAdnIm3Xc3RDRR9YqVWXYggPzLkhX/cC2du0YPEJSqBl0Pblg\ngYqcQSJgqdUM+5RJzrRz4PBspjP+9SQcK4xCuNNXFfxmcI+aumf9aSATJKgZyoKU\n6FU4PzAfXOj1a1bH6vuE/uqKbI/GXhYWhFX9Tooly5B+uNNSQrmGTrMisg4EbaXA\n3h67FRV1qfW2VxMdZ81iiDENo9MGQxBeOkyhZSnJaiTQkkIorDYKzrTKRE+ICWpR\nJtrZAAECgYEA9UyufaakhzOa12W1ujQH6V/2gEULwdoQ61P7ThUPE+KRV+6mDpmv\nBqdGexzXc26HH2rxM+4fIxSgBc20xWPhSz1Wj5+YCQ+X9aXYeyu+1BFIhEhNwrbs\nUNOGZnIRUPF8OqnZwrK99zASoJVsNBEpQrlPTguQwFoASLRlRXznEAECgYEA8iJw\nkdh3LhnIyN80++vV7lcRRjTo6sqLnilJxYrQzRHik5nlrD29U3ijajD8ySuzTa9k\nRdpqr+3i3EZ1W6d+KXaHWUOvEu+v+8r781YPsCExsdurEHIczAuLJpxjTUKjuLFV\nmiCXK8lSoicf2hM7HXt5OMZ2hij1UhPu/iJfMeMCgYEAp+wWh23Ms8Ff/pRiE1YL\nFHfdB3I82jet3WtgXdOMvtAbL1bv1o7egm+AkEbuNtczKQ7MkAAy0JEN+usXFQmK\n8TbMSwW1zn4eSMt/ptILkKFEQ0o78U/H9ozXNMphkEfAA2jC6cXyxYTjO4sOx5X3\n6PVylLrWHNd/2kWkDi2zAAECgYEA4wUTUksIgvq93Egan1BgxhRwJ8kX0HuTJ/lC\nJ6qdVog8TXNveWxxhD+Jx9iiSeNlO6MEetHsUYLSvwB4rDR//1QYVsM09KYx8/w+\n+clo8BRPSZjR7JtxEIaKMutR13BxnIr33YhM9ErP5SSVopuBQ4UvBOJe4i6oxAnY\nfAGyC00CgYAagNclzfuMaoELuE8m4lI5r9iCYA402iy65Fia/JmzPoqfb4MNQd5j\nEX2x6/V7IkT4RF9yXlX/dmvgOKsc2KnvTK9S9H6vfrK+e2ztFfnEBr8DSGLMKu0K\nkvgYPx/s7lhNOLhXmw0cqKsqrbN7Equ+PFEUYw00DUy6VSTQLR5CTA==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAsEHpbv473kZSVjQqkoNNOnuU58IGW4z0nXGdAHPZFYESGgye\n2Kam2GK+/qN8xdBx3YPtz3HY5XIny87XBtroS7iNRcCc4xMnv2BQhb/R/IxBdS1R\ngyQGy+aYjg01+lg0Q5BVakDJeDH7KIsQeEZZSfWJHGbAbmtzBXWDK4Z4AsGw8Spu\nfvZlj+PCAm2uA8XvQQM6a+uwq2onLssT9dOuERgzasMIevi51/M37gUy2bWkuetn\nqITrssMvio+nJKyaJJERcKvFBSWp05MXbXCxZ2BrUGndIotqqRF+XgmoqtxKyAlM\nGudMHkKyJ52pG6NZFMsLyv9ujY449hmxgV/0fQIDAQABAoIBAGBGwJGmQWF6nAMG\nX7LEEe0+D+pAK2Taq8tMgev7W7rJU+/ysNm6viw4VAeXmdIbt/a916qWj1lVb7N9\n+ip+bKDf4BsXyRqoHfAFVjaYvsTqf97XsEyn4yaBgRcm1DM49uNo9Z8iYGpNMtnE\njGyGUMN7YTeoULh/7MYwmEOwIeGurx7xLQaEHDwh/siURt81km9gnBDtTiXOuDfY\n2qAVvn+CkObXw6zw/Vbm/gpixHTSjAucdc1i5XbcamnMs0b+gGv6j3lj7Y+ZM7U/\n+e0m8k7JP6/sELFS4/SHdBacoE4+W5n5VGjLr/i/jFUUSF4bvzE1pFO58EmVEKgy\njU7iZakCgYEA23Tx85GB78K+xIzbMyebieteui7o5OgLwLlVhpHnsm4ArIUnU0PO\nhXdhiknAxoF+Hbuy3Wr8z9C/Kp8rrkH0l5+9RLamjQAozpMmvKKEAOO71hmKqu86\nS/gjXK/dCjEclYBTAdIityWRy/QRrXu5NVD29Dz1pPzaBOuS7Qz8aaMCgYEAzZt0\nggYXB/E6XaAAAx+VlMThOArxRIojaqjiGs51U1ELmIHqS9gXcA/t6IA0eE3tHqBz\nQX/C7c1SJP+Xigsq8t5W6hcInbL8dgBOuc9scW2MetULcYzsw7tGbj21dWSE7RJb\ncCgDuMvI4HKAfNhAUS7KbUYqmN5mS8Yx4sBWS18CgYBFRAL92oZFc5UcmkyxN6Yb\nAOQUJawyW58iBB0fbTvREHE8Aobn+/XaIFTz/dVPkh2JKu4IfrUurnc376csx/p4\nRN3LK08kH9HBaaaHUVASq85khAlFbF5dDgOzRHqitd+MvET2xSlZ3wzGb9GIjQ1m\n65gWfIsXuQrjFKt6EGg2uwKBgDv42CcE97rNViTxAo/mqo7WTos2ReGkGWiHEkyX\nZAmFXkiBvIGa6ls5ij+DBcsBj+SbEINObOFIPmmkU+NkFob464kkZtL43kLdHBl+\nszbyqUM62Tepz3XYLlcBkOhJBazQ6fLw+QTyDAnL3EaOHVSQvWLaUJp+ZIA9s1dc\nms8pAoGAA+EdQ4S8YvqvvQDlekHiXScpSY0keRx8fbfDLpl1QokP0UFZkNvqqTGQ\nqOzNgowbglPQ7j0fWjQWUwWSFx94Xfm9+kOYU1U5sjFXOG7sLUK2x9uMgJmzLlHV\n3UBF/nJThleXJgQNlBxtzkqNTpNKZlZbpUq5JwtRHtWzdr3n4Q8=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA7iGdOgeRRowL/pzpYXvU9BBZ5iAN0khf+H6J8xY5XcFTcgYa\nNuc/YsvCvUSdqQ+yA6jHyqprKnv+GJMMDo51VtouR28Ualv9jHDaS0PIRVETCyIv\nGl9cWUygP4XSL4E3V0BRtfMcIFsxlcdLArBkn9zLwKd2lb5KHsv3VMynm1Hd5Z+g\nuy1Oi/Aku7/DrE4X/NX2bj14rV5oWNcJPR/Z88J7YkXh6b6JcodAHSib9saSI4Cq\nBTivl0wQ3oRgExfaubD8SAmDP1F1Ga1oNceMsLPY/w/ud7RQOluSI/kP9WCGElet\nUr6IikAIPgIOLxupKSgrFVOJ72Qaw0oSFya16wIDAQABAoIBAQCKqNYVYSytiIS/\neHzIv8Nrbk36mvWRWYVQe9Y1zt4OgrDlNzpyd8rH61/0t4yaUcJVMSVNFFicf2Cg\nzJb7ruYSca7692pK2E/WRCxIISrvedr946TL1XU/pke1VWXjNXmU+6XYrbvV92Pz\nE/3Qjge5smaOQrThUtF0B054eUarQCvL4ro+l8X1FA7MO5mT6Y5m0oYUfl7VsZez\nDm/llkTGVoutV3c/dZL4L1DS0G6Ay4QKlhmvN+Liz8ZR1gd9n/BRzV4MRteOjcQL\n7jleqY3oeXUhcCDGpPEDCNrcSf32Nf6R3dhIUBGDwNxBPcmwCY89TJOj7QMXC0hb\nhJ5r/KaRAoGBAPdcKMrURuGVf54FKlI2ptmhM+SIHuMJjnqGhPeBUoYNV0E5H/YL\n50p9DxoxPPOeQbftlljIEFQAHh/fihYxv8DxD9Pj/TUMaoLDvPSN/5zuuXiykvkp\nq3PamPpq9k105tYZ+Uv50lWlvSvvXn3y22qy1EweCTdh9PbfFtAKGijTAoGBAPZy\n7w5nonIOKslMjfU3We8D0gQBclEbMvguhSro/t3y1YB6qUL0rW3yPZxDkHfJ6UdN\nX6p430aKoxHLy/whyf8jvRlu9OoQ+bnmWX81Qxe0x7XKpRmXmd2aE8rF1pi4b/EH\nhSyOyD4p+W6Mz/Cp0WKwSiMv7o0aanENkIMco4+JAoGAYQ3F659SmFxCEExSiKNs\n3+MCK17FT6uGPcjeDwaszHAbljnFspKFE5LyBALhuWyUuIf9/6Ov1GWfbD4XPhIS\n/K8Y2LiRtujCOcB3OTUl3/tCxnq6dAzHetOARnEFZurBT6wtRoQk5Pn8uWpYFu7c\n7IZyCdOXJiXHKb3qg4ITNYUCgYEA1r+SBbhSehZUPDJkJMBFsptenrTv+zK7OD25\nNonuxFI8roa0BS8WDI8gFpFdSrR7fPE0ImTPEjXuIKy4JA0BajynI4lnCqseIFpT\ntCAiRX724zcgpGtQ3SPMiU3ZUByvFpVZ+1izrk9/FVDIRUqEtP0urPiwYu4YwEh0\nm3sSHxkCgYBvcdgJdn83Ut+2HQBuduDloNAdw1CGjWvYjU+5DY4d8CF0UHTyggLA\n6toEh/2ORTaU2Eo3ydt08Ro/yDvq5mIOeksf9Y1bAfZDVpL5CJ++cydHWMIvWeVT\n8BsJ7MnKUA8UCJXthaZrFpASanvK6u8i9UaKpNwMssO4/dFEaYgJhw==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAwIF5PyaP6EjxnNIJfJYNPLuF2x+rvpd9wDEHsC65XCyugvU+\ntpG4iCqkp4GXpXGtCU38JUE+4KOmGbqPAkGNBVHN+CKzem6UfqpY2gKucwCUHLW1\nJp/FiwTg4HOWS69tCuQlkC0TPL4OaH3+phJrtuyg2isiMeYFdvWYjnaGvsYHc1Ig\nE4+TH9lz+rPtUBv3aPZgoxJzENoGcGnLtWyFKeieKbFNfn7OFfUlVcWJfjRINEMw\nK2qKbRtVKiz0zcFyeNoNVDJGk9eWzjMG6Xp7belU5L5WN6d8yLoXsbp213/Kf/5g\nfWAn0IBldNzWyFhNzY7JTrI+bk/6IvqfOp8U6wIDAQABAoIBAQC7u4gqdcjRlpFJ\nheuKfCrcl2OqlfA/Ji2USDxfLLHHmit1uwU1tlZnNG7ujAr/ZSSCdnL3QfG30OAj\nJzRf1t1X2rcqzWyKcVyPSkDsGsFVlbLbbLkEubtEH7XOLercVG/atzOKruqbyhzy\n+ixkeIJjGdCj8nyefNvHbysfg6dUUsnkrQMZAksIUl8fT2w8/88ZLS8nodUiS+q1\nUO84yeIrDAoF5txsbEuGZvzE1T4oyaQrwy2BxF8FX6uIdnOkJn5EQ/O5snurb5G9\nC8WyedJEXHgiH05tfJuIQvLkBuCY1ehtifadbZfHqOBWx/F5kAG2Stao78vBTdb6\nhXXO90rBAoGBAN44+4bTi2ngzxEUip0px8Wma8eyRFImzEaV4mu53IGEroKLsYTi\nLMbug+5qOPYN8dv2F4TsQN3f3c7vQXj/h9jVwQotLEX/Y/7nlGUQhn6n9ZtOFE2x\nz+3N82NcBCKYeRBvpcS9KisA9THszzckyTndx4cn5kLvYqfPK4ey77bPAoGBAN3E\nKga4csLSrY6Rgb2P/nXJF+DcOpxU2jPZIuYMUTIviUzs2yXgsxbw5KgVnWtLkd9K\nmzWPpmLoUJZAbGmYGBKHPhZbMsdC6yEH7GRk2//bVJuyctnpW9USjxhpmsCvN/EB\nb6GmPQQxHYO1AokKs+pZqm5TP4O18LeVzUvO5gclAoGBAMd/WneQ5IC53MsVqNct\nEfZj0DCn3mPuBsd7eZiCc+4mglAaPtTwA4jXycM4w0hJ/o4c4MJpIlbNyiAjdKC+\nrmNAipurMCrq+wd1OSh97HA6Muko8FtLvUJc+RqDGgdA4LabtLLpq7iNlJIsaV60\n/J2hd6XjcPk4VVYXS5n4TvHLAoGABJk/wdnrT0ztGmq8BQFKZojf/OK0OG+uAqs7\nBQg2WtMPhs/Wj4eETzJ5m16vX/02Liou48Jx0dU9bpAxb1MfFKtHR1Fm0Sj7FTlD\n1qUcxsZ2j6/4n7ouFFIJM5llAx/fChlchj6bP04BbazAO7VEG9T2KsmBJKsOLdBm\nbG8C6B0CgYEA2ID2AiCaKlcTAm6Rl2mnInK5ZQ8bKNiEEur27I2XOEpeEvF7a+sR\nYICfS2L4CTGYMeslhznADFopVMydfOjMHliWLpZ+rEsWHk9GzHnStH87EVdPsOxX\nGttwDMhlMMS/X5yrm2RzDaCFRwR0gsiGlyf8xSlekSXz/E4HUPARKh4=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAvEGNJ8oSRuM9WPk9FpKBicwxf82mGgNjwXKaniO46cZmPJZb\nTqxjpkpGUIAs05hRURw4r30ZX+yA6eVQ0c3SJ/epfJkT6U38c6tumeFV2CQIrvFt\n9v58T5rMg8ETSFSSdYAkyQhd56D8jLY+DXcbeeT7XBc5xcOMoIWQ+6QJlbhyCHhm\nm+7mm/9gYce/VrBE/KixqOjK9uaTBVJTSpQFbwl/4goWNplCqiYx+s1xMaMT7Nbg\nzi5xfnPOeos3R/TQWux1WVGrAOnjqC6KT/X1m+1I+A92xN1LeJ0uc4bMeKMLSejo\nMgBRZsBd1u8zeuuuKFC0tovofusnnEsLlBoacQIDAQABAoIBAEWamv0WKnHJPPpz\nlJGO85QmSjrtpzdA/5YbsG092fWdZFmMYhARjvqBxcOn0GsfixlJLPA9ea9Mu1E/\ndwVLayNZ33FxC7qcGfYOcWM1vE6g7BD6tOS4rO+l1W31ahK5KjmFepRVopqsnBPO\nodzKp/8GmaUv9S1YotsK51MTEHQpTqIirXfQNpG9e48kdNG1ACrVR8TTkoAlujww\nLFqJGAr/dzKfxyUFEcbZt/VPWmnR2Ah0q6JN9uv78NU6SXX6hk3VWXq9ofH82+oC\nijMmNaSEYufOBe7aWVkLPOZbA7bibXgra+VUX7xjlLeJSlVHUo6kyhoC890rvmBG\n+KEwcP0CgYEA4bIfb5qGLW8/zSt5zTFnlsIdQ8qIr6rb7FYtk+PzN6U9E8eWylRM\n84EVb0uuwLcujV8m6+0YSE4RwiIDIjKe1xbzxM5MNsx3+PwAP4Iv4n3J6ccJLxYD\n59IOtRl3TKpT4nbRSIfysbkWZYkkliKFwkh3ZeSgoLI0adq8UYlH2CcCgYEA1YiB\nuo7d8A9ze5/enM+CPWroCRErESWlZG6bvDwGgb2XMaAOK79YzJa0HkLQo2LyBJnf\nM8Mjm5OZpKCttSEgYpg1rY1dlGkyr7ND384TZuahJLkPWPf3Zvl0KEJipRc83iUe\ns5eq1HaoyRkqz2Vq9m3exc4NPNT4H0KSyN+xv6cCgYA2kuKlEuFVBSyRSOz33/Vj\nb+axqLyqdY8eM6xrsVfzYaJGMSHWJNJCozjyo4NLGEBKcPKxY7BWc4I5xg2p7EGa\nUvN46EVnjsOH0i+hK7K2eqIjRDyFRTaGjLV/tvuw7xK/lOmAN1dJAC9ZN/M/1nMv\nimRMoB7zHIQuAHU+5goYSwKBgC3BSWMLV/0nomw8Dnx1bofwj3XZ6uuKsIz7sn08\nM3y7fllQ3Zh8MO/t3vyY/pH0Er3vpsc1qjyc/RAsqewlBAZ8bQwaPZIelJDXjdS4\nGKo1AxSWXvaGc8np6/zwdKjBePZ058Y92ooaPwKQVP8J0UiqtFCYXXfKfrxcDI3k\nePcDAoGARVAIWWJxQqVXhsGeHr+N1X1Hlt6Z/d8QxHvOFPooGDsvMscD9B77Bogh\nEvhnY4wfN5oPZwgQRg5qOUe/+dfoYieYMZK3ZwUjCl9DIBhlBDmHkC4CruS4KSzj\n5vTOvLSuezfl1RaKqTBrFWFCQmKiQaSlh/nrNGL0NKpj7HZmtrc=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA3guxRjn70riTIXeIpwooZTmF4/A/1CDUVWNDz4U+CKXT81up\nR68hSfqZPJdJLjvXIny2cOCCndIJfMLCAyuynlU8ybVIkdz9hmgsDlu6A6p8phgC\nEFkbekpc2ucvPKb/WCN7PoBpVGi1TBO2caRoxsKj0eYA/yrELTmh567V4rQKHJjR\nIBP60rTNJ7IaidnCLWmHgQuZS1rIpAlfBOfypit1zQIgj3G3G3ov4PB7GVGpJ+qq\nHLqp+JZ0twgnLcVIDYE9mttNKYIiVhljrJRV1FZ+Wi91gsu1lZo1EHTGqRCK23L+\nV2o0RshFfLDIGckOItJvpY+ZVU/JTKDT8JQHaQIDAQABAoIBAHvPqz6MUGV0M94i\nZ9k0acffpk+uA/lFgQBHxGTvV+K8jI2S27Yz6ez1RgKqHM0h4tjBD32rQkm79zxQ\n4Juh/wwYXTff1KI4xkjuxzoOXY9zveNjWatB9HdhBZut8wHv4EuGJU95jMHbTNQp\n3b5KPX4uw+fjR0UA1Lie/S9RpsOsNFOK8c2WM98ZnYHnpZhYn0hjRbudNi3SNP/P\nMVec4KphAcfngULMUxxlRLMhbYabbQoR+/AYowy8HV+oZ0lSib1GfTY84nNkfx94\nPZBZJ2Z/ydTmqsQsXLToQv5sQ4cbp8ghBvCJuwdTr4U0ZEqnP1AMftP0WdcRrpMq\nB1uLsnECgYEA8xkOb8Q4hpO1VwB2zugLrF3z928RvHIBPz9e1LOIQBjYyaOfqIei\nBDvqO+rhZ1r/Ag5NSBMumQ3eIwiPDAwy/CHtzE8foYIL/h5p7Eg38AOxRafX9R4m\nr89uiIPoe9yQywJDlph6VL5roLql0wBCor5/UbCwxSLqitlztRG4FS0CgYEA6dSa\nJ3ZLtpCEUMhVs9TC2BQNe2XfplbkQoFINfd24i/xURPVeeKEaW4e16Y86omEInn7\nRRcZQLgsr45cllLl5iynqfNml3EFxUB+54YqB8MomWp/WdC1sHlSMpm1nh52IbdJ\ng5pAdbjVplP1uG6mxyPgPE7XEXwMDZhSaCvnmK0CgYEAn7g/vvlKNkmIJLB+XvUg\n7lAQgeqs2YaCAT8unEuU4qAwl1L1DHKI5YVEIbfx/slPsNcauZc4z4DICXaPHdhK\nC1RzfAAkhSX/oSf0ajUM45pZd62Oe1MjEfIU6obYUcEaNPe41pI6FcHCzyiy2M5b\n+bd1yYNwsSV+ulmi5z3MF8UCgYAalaRNFkL3Z6jTcmNkBa19WZJK1ENAKyLaeM5X\n7GGkx6Sc/i9IIzvArNdu1ySX2bifHtU1frHNTOMvtnTKVnRy3eLWDKk+UIhCa9nB\nwtwmc8a64oPGF1eAz9pLjHQ8nOP7Y1ZFUxU/DRulRGhsZpApleeZk7EZ+Mt6BDZi\nR5OWJQKBgHbrKr9M/BdT4KOnJ3z+FAKDXfwod1RBMt3z9vIPSFFVlNgAtPewVAda\ntg5C8PB8gXdxzTpu6AUgMn3tGzShe93KF8sBLH09bYGjZzCSUN71/xPH/q0eWNyi\nOXj0Q3hlT75gPCWO6HQYHJ9f//VHRmgbVWx3IwGAe5eM/yOOIYZa\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA5II5NNnSjp3JbKsX/QrbXDiCzy9Od9dEqx98kaN9ypNXnbWg\nJhE++o/adAJllQj7Jzs5eFuZUji3ddF5fjFfC4vi2oGolRtGgwQZCme1QITjuY8m\nFjc8j6EYpoPE0DGtOcfdQcEAVYazIg1hOHxm6JdE6LRcgs6D/oHlsdrYL0Ff3DKs\nfOvyNgFYjRmLxLz2mNKbPZNed8L/PwIvNdWP9Au7UBJw1/UxVgVxKpQ9MuUb0kw/\naTIJgmpuhDoZVIgUt7bFqL6ygPsUaVN5rccB3PISa1kluzlVQg8le7tLJ8MeEpaP\nEA0qsPGYaBAUu2ooKBM9XiInVsRTrReDXejaXwIDAQABAoIBAQCvAjFwEdeaVKxc\n5tjTe0ov4jQFG/UcAanc6ZuNK0r/KEO1TNJf9VtnqV8k9GT6dMtt64UDchwGBdw0\nhv9mr8dcX4GZ2i3ULPSfCTdoaa8+v8htaH5CC4H9euMXPNoynalPFIyXfIqV9jkh\nvl+UVGq4fopPMzubDzG2baQOyE5EOXoG4Yb6ll8FjrOqmdi6s2JMMjZNkzXRC5oW\nTyA4SwjsdI1IXfw5wHZudVkZjOHO2RWHtdPIIs8D7UwFBaTCsAwTziYcKTVuMPOG\nlQENq0aiZEjAIJ2NXOp8RKvPDdx7Ae4F1wnZYysNn6fQbW7EK4++UlFZ0CYPXT//\ndC/xiZCxAoGBAPLWEyMr9sc9KOhUt7Vm0tLK1N+H7olDHuLkI9SNVmkZv72PtrAS\nDhJ4uwjqo2OaBmVI7kDK5S8PtmWUCOXlycbiG4adfXGsdzGbkNwu/oNG/XpD6Ymd\nwHZ8li/4c4lXvW7k0yetnm4e/AaioH0z5/DmHRmWWaRGrH/mAqUFPnvJAoGBAPDl\nUfv7G4D5UOwCJd8OFC6HFDKUGgKxqwF2Ij3f/zrOyHNMeQvTVBPeWPiLEYqJ4CeM\n5Q7+4w05F/iA394JTzCYVdDuhfD1CwPce2v7qvJijMBSb1IcFR0sOkqFaKgOgldE\nDnORJXARB6nAop/pQnPCF1rL+716rtSTJ396EejnAoGANzHRR71GaiLMmYIn/2NP\np/nSSLxsy2YmLwwL2NQHP/xKSJvRulNgP3KRkIVEfe8UZREeljuHvBqmKpBrNVX1\ndu8Nsps5WA0LSotBccp8rvoDwzaMbmscw9GtkVm3aRA4TmxUUkB0MjN4tUlhkROr\nNGYZVyeFWIF309pD355BhYECgYBakOycvpBdkd4hjMEuNf3SbEhkp5eL0b4H46EG\nADPVqS5lYqfTkVT3z01FzAsBOcrDM85eL+eu7fscG1+O+5MBIxSIY1SJ/aZxgjAI\nrepcDD78g0GE7jfl6t3TbIqnPCU2p8iDHFul4VEvOdQqKKkqGhtrsGrplrkqBFUC\nMSOQmwKBgQCq1fDbouGCnTrC336MLEAe7mrlSZk+nzNM4CufOu92Idn+fqCxSo/+\nOkTHUW4bRq9H0VOOvuHPzlJjZojWd2C9A1dRXl7o2aXtnXj4bDSpUDMOejEYSL4v\nNnzFA59BItkXOA6KaRhHnOL7Sgr+MC/zw72cbA10xm0/XKaPjee2LA==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA13mWtPVWot+Dp25w58n7BLpLORDi54rGWYWI3iZz+15/ZjSr\nKS3xRvW9TE1SXk740NuFes6ksH7wDwRYTGF1ZIGOsOtJPsIwKAP5cE1HwBF/sSVx\nDxNdUkY4O5/2YVfD/Jj7TKLtDKqpXFN+wNMfn+PqQCQaTw1QXm/o5eoS5fxJtrZt\n3cmDaCkmOcd29/pws6zg9v7zmt3FKxyXe26kSbmkIhbL4FtHyUAHIL48+YBH9Y9l\nlFdhSnOMLzxeMa7/RdePOc6UBHnsqnEInjShcTVm6VTu/5DOj1GWphRmNaRiv5nn\nq24eKRRuhIetma01xP65OAgbw4xpmYoykGmkXwIDAQABAoIBAQCE4L92gqFVuZ63\nvtyzpBQ/ex7uZyJ2hVcCV93qmS4HAuRvw+UVdPaIuvxstHYqIGtCaU2sSQi4qt8h\nc518JNG/FEJJih9WK2yFEMZfYdjwchobxSoukNOI7DK7Wk8XAoejGzhFr2ed2xSs\nEfVltgyXjnaj7V+oBl7cjgjC2BGlb0jc+GyiCGBoy95CAtVPoRStb9LPC+tnAPMU\nqySZ+Zsu0bje66CrshswGA+scV8D5uLU0I3AeJVg8nHO69W3H3OZXUZEbiASreIz\nWQHIaUnBrNIVGRyEdJ8WeaAEusYy7FIypwoUs/PKOVEDeWWEr5kAMqiITxBd/EtL\nVx3a4t9ZAoGBAOtsEZVs+Ad7lkNITpDLCt0jZ5p8wTE668NnZqELmGKSbtKlx4km\nzVXjlaPqtE8F7JEb0PBFaL0qjdSbDuSHzdwGg5l8JSg2KIssfmsOKic8bnzIGUC0\nk5cSSY68QQRxv4lN3enjOdef+1b7DlZ9jluCY1OruHKkcvBrW3DdwwyVAoGBAOpP\nK85iosJ2KlBBNSblc3RIFppOZAc1vN/YyjSOcWA8+lY94dHLkhG7YfZ8fQbeajA6\n3xUg4T0c9zbE1WFfNBsaR5Dp5EAwdibvuQeaK0M2UAWJ7MbxPoAxU2scwTC25idK\n8rIlYZtbVqdAIhELu6WkgWiojuxT8TX9GH8pnDwjAoGBAKXUlf4/p+b9czcps83i\neGCVE+GKhzt6WaBnMa+3TzwAEfhntkox/unFSh8QsLoJXj5I7cCR49l8JDPilXb9\nAlE+dWykOjg9Dgoa6WHXtHG2680R1lfx2xTLWO0mRlYvGdBJ/UtHN/NfSdhXOB5M\niv6Cthy2FVML67rWpK64RzCRAoGAZBpE7dyoCopvU3A4ZBC9PZ2awnvhMk9cDQpS\ngRemCmai0PuJIZdmJVs35BWVWLznAwnzytQMRo68c1KukvYYjcexcZDrah0KLWJX\nW48bEpD+qQ8e8HOaryvjpqxXQReyxnC3tTJRPA1OvQ+iPkQT/BWHEA6LWR4XvGgP\n8iv1yp8CgYEA1SctuMq6pYkxvkaCImR22Q45MBnSK/4x3xo0BtLaPfA6MHL44JTx\n4IEDPvlA1UVj4vOffaImsbSIbetOR8GKtMKdzNBzgEOhsyB3CC+ujpDII5zb9npz\nP0gSxWGTqHJsmHl2/2so7lmVmwnr1NC/5x2d0RRKzRfsWp1zbVYO+0o=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA3NRB9q+QXa/U2UAzFVcePGu8faNJnZs6RvYj/vroMU0y/lAf\nH+M0y+LshIvH0340uXQFr7kICUZqmLCV0E3it5nYMj7bNxL7bcx6ymGj34L7Ijud\nOoMj4sofwYs4dMoDWoMMYjNFZpehdCdAQUrcDKgtY+cLJy92gxB+Z/L5zmlxQCBG\n6z+/LfZTsp9o8RDjHEAdzZu6Or+UQt9p65yy6/vX4AQh8QkLhv1QzJVTZtV01U2Z\nmHudP7JCPEqgFqp3hcbknw8DySj99Nwu/RIuba1sfS6VEOCC3cPt+PS3APko8rLk\nhTrg2nxoRX6VlpvgvK9Kh2+HfhD3060puaCq0QIDAQABAoIBAQDAn22CjpWxAwT5\nFCT7Amjfb2KVfO9jyNlGuMGhUp9jEeHfL2hW2ktdZW3GGF+PvdCjy+6B3zaBUKj+\n01l6v9V5LXsGKQnyul4S4oBhNFZLSudgbvuw5F9THdghbR0r0mKmexU8O1jYKn+s\n3+gOjAvyBmjBcfGqlp4bqTF53tnED1oxmTrkkpXr58LCw2oUf5NON/v2Q44aXe90\nqfhvQz/fWI2cluzOrcL1XxAbcErAyFInohTwiXW3Y2jaZSBRIBHUTl7QH0uozuQA\n04h8NGwWv4EUSlWb0g0llbitPn+02sokp8yu5Ps3YA62h1PMidhQW9rsu/yP4G9l\n3Y4rzd2BAoGBAPxFkhFkn0qTVJT+GosnDXvGkVeaoAqcY3a4kZcJaRNSv8n8uJF4\nVGqaopMsz0Ia3W8TfA0ZZ7wirnVPTMcxP4XgAH55NN/7+RdX5Ik9eqdQfaz+ixH9\na7ohUlZfo8ovi6IN5IaBa2P9PjhrGWCQrYNXDhqcRRy4sXZY52zCCboZAoGBAOAX\nup8dCt0q4Ciu/f0S+TQGSaNW/7b43SH63H5trj++q5oBVRsHQvlPAkEMNOtzhjpC\ncuwTdw1YVjmTjagS58qfek9DGmcd/TH24enV/ybWMzuao5M91tohihPmapAaxz9V\nuCiNGq9ZhlkYNkiA/kbH/ykghg/UHqfrQpuUpf15AoGAdBpIuXyH2zinStMPwE8+\n42Jl7d2aHogPNsqHJu4Lz/rbjcij4T9yCoYUoIuU76WRl9dt1lGE7o8/n3nZqis+\nQwzu7UY3UYaUA2xOH+ONmYdv77hOpP7vH4DKdjoaFamAKBsgu87AcApTIoAX89ti\nH/pm4VRecnOGKSONjv5Wn7ECgYEAjaHRvLDxZdmsdWosc2AjiyMSNZ9N3Yoq0Ugp\nReryf6117c87x+puBP5PsWcSp2GLi5Tl55lh3FhhnuBIXtscDu9+sUv7CwNkPMkJ\n/e3X5ubo2JojGnOyMImUKIGwBkY2eiwmSdLs87jEddkcrfSBQaEz/v1kXeq2OXEy\n1vw7cRkCgYEApsM68w2f7zwI7vDs47aQgxJfxRgWX8uzETDy4E7180BYvhGuzy8L\nz3J693v2mENyi/8kPdiIdbTpzVsrL12vDd8Zed5I+vX6Rqsf+buM/UMeGz+Iid/X\nJtMea4rZOtxy5OqkZ7ENX5c0dBTLbGDSJn+Fp/bzogbRrAA5lMeM/wM=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAs6+waSozW7XeKIn8HlIuEvtuTH8WN3Qw5oJOYN4O9uPYZoE7\nRvfMtwkoNLyL8sSKlo42X5v4yehVx23QjPRpZtuar8OSHE5hfpS6Dxeop9FgNCiA\nxnGu91qox/0+P7SYkPDT9EHlY6mdFqA+YYaQ6fJ3a2lDzwMbW1e9Kl6KCB9wllJi\nG/8o9nSnNP4Ro2lJCREwjRc3g4nJaRJ2u/rhMhpAxp3BrbgI3XcEG9+C+WqkVeXa\nSzfyRT5E5NU4miiCPq9D4yhNxjQNTTghdqPhI8g/6hh0o7lm3RpLIIiv9Wgnxo70\ndgkQkbKtBXPYTHXhwvmcpB2/ji8kEoiHB69ZywIDAQABAoIBAQCRBbVdy3+T/18E\nXQms9yrUizVkbBXkP9TP1k+oAtEVFqOrZ5YOGyWQfMWEesCA+XvZM4JS/DF51BiX\nUtxFR2Zia8+XGEMusVwiq6rXT4MBnw0ES8RTZaGPZg5fCtdY10tjf9uxghW/sD9m\n6YIveEoxZ05W1KizxZAo0tGMBq1lC5A0t5283qLQImzb94EfbPYWO1Z7ffO5X1eD\ny5nkNjx+EnuyZDn2brNMIO2ym9gIwDB/3adrfkQCcgZxVffaOSqJqVp8pEyA5M5k\npuoVxgRQORcbrFg1MKgAfvbdAWujApXEuZwpaA2Ywf0PlG9bhoCdhuLedGZenJ9r\nocp91BXhAoGBAOLatvJlcruS8v82f/seFkEl8kQyQOGp/L2y/Q2Qhmj1dnZp+WCh\nBjXv5yDRAoGj63kF8WxsKbkx0EVpB1FIkennItr4Bct8ozjxZiZ2IAXtmxlSMxf6\nfANwAAzwvKy8x9SryaOyk0DAlJuWNW8zSPvPvD9XnDOOD/aBVX+zmBpzAoGBAMrF\nm3Ggl9ThdoAXo4OlSVMLaR/qLq+kbt6lhqBkv6sLWIqT7uHfv5b/KKKMApkjbpxc\nmIYGwPE9N4kVi6tIt3k1O/1n2rpnmvU+TOZfBJE86BvCDcj9QXerrWW6hA9bhUkM\n+HCHCcpuVnaPk5jtFblp3gbcq36vmdbnPJT5UjVJAoGBAM4QlHxtEKI4YsjXLbvi\n4Z4+JOozp+IUskNy8hC/RojS58+6hc9zlZurFw0lLpyqVLwzXND7E49pI2HZfM8m\nF30b+vpir4je57lS9lJbnM0lv2xp0YWnf7zzoOggSZKzTOXvQhOTo9T+2j7WXCVt\ntY8fbUUwhPADzJZDd3GvpqvnAoGAXFTSzVDFEkh//x/EcyOGWewuODwYeh4z5eoV\nfV+i7y3BaYinE+8gfI8/X5TFchnyOfzn7s+Qd8jaSu+mo53+/ZBB+seZj5uxsc5m\nkdfeaowDXPBl+olnkCh6UoI1tBCGhpcDYan7yVOox1/lY5NBeyY71YASDyPCt1qf\n+W32AxECgYEAsJntcxMofGVTmzvioDjFUxGCvQa9br9uWdC0+nMKKG+AlzmeGQfF\nQ81uugC0gBb2PWhz24gv+kBtbYl3ekQSmgXOu43zmyXvmNbukOveX3RzkSPjkiX5\nmS2UXLNU9KubIsrZF/1qezkiBcNsLBVA+AHmWe/RarwoFSf4cdczSEc=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAtVfhNL8uhHB8OG4VSJGikl7NOIIe2YcpXL4ngyTEKaNadwjK\nIPPCt7joOLyI/jORzxUkUj2YhsHH3bP+/s3GclgakbVB4Lv5qU6dVKXLSu7XBKTX\nMmooc9k3FBoCZQj3jy9UeE07Kt053Fo33itTfy72mjA3oBPXD+Eix9r2tNpmneX+\nhg5Eqw5N9Rm1emoxHWlJuT/2NkZDmHmrZjGIm/eknpSzhFq4g9+ryVoipmEa538X\nKOoruMw/28Z/i8tE2j6S2mFH2llMWX5rXn+IR1MSBhINDHikWhANB9c/yjLt1VHz\nPZmdMvZZFwoPUTKityUphK5OGjTq6pNnhnTNcwIDAQABAoIBAAP0P5fb11LR9RNx\n6wrjCg1w/b6Or8eEd/VqvAfTBGf4BN/1lBq11fgVkxKxobcRPsuqJVmZuDqv/QCr\njRLXP818eE/XCPGyh5YWfb27w7wFizCxu3OA9xLXmKjajFTRpjw8BvZ8kJ61pi5U\n1UUF4tMyHfRsurnF1PmmqhB7iMeWtiKkzIPRmxg41ZsaCgwwGsGrE02NateCTFIG\nI2OPlj7EIoxFqy+4Xsyh6tUozVzilnnUD0o88dqfk5+Ndijje85mFKr7Kb5t1Sr5\nlpMFQMbF9zr1Fc/Fx4xXxihTIwBS+HCVphFvj78oAYFZf3aBSv0CusczKFgwCMX9\n4EqrgtkCgYEA2GrBiSm8Eqn32XiBvPzUI162bi4SlFjWMGfkiWYIw1JkxTpnHuro\npIgt3cUYtY4kiliANHye1CAkU8SrVngprWR+EFzNv6p09BoPC7bE5yjrl6kTi/l3\naqu+3tCv42GYSDFvw/UZaVKw4cTp681bzX45byKjJ/K8SdoeD/SCk9UCgYEA1oLf\n0Sbp8p4IVVOh+eIPWH1XI85oQDLdkFr1Qqi9fy2PDeV7myh71okXefu40roDtezz\nk/l227RYsgYSbw9lRiYNWf4N8Co8Mtc3M9WfE8SE23lsAQKYub+i9fIhNir7m4Iw\nLPBtXA4tV4xY9jDCUmQlRZeeqwTUAC+xTigbKCcCgYEAnGEnz/E0UVg4HBCTzvfy\nYkyCDkOEcWVwADJSVWZBVsImxs1YzmJ4EMvGOMW/ARHhHYatvT9/lVBUb6NhG25g\nz0YaQcCNe720HN7nmFNNHKvY8RHfafmtbIsFtQOe1PMkkqYGWNa2sgBqh/k9/oG5\noFdhMQkaVDd2DvdpWaYY5LECgYEApJRg+G1szTKmniC+g6QHthLkippQrgDYsWq4\nBd7FFRB1U2ogPdj/uogQNQ0Grtb9BdW4xG1/3e0up9e0yPT89Kc5UzRyqTMeYLty\npRhfJqF+dY1hahz95HW9qAmPpamwxY1LPQ9yOFDARQ1vK+QZE3G2jwBXQktABKyS\n0q4+t70CgYANdLc4/zWSG5M3Vito4CJnrGhvFIcjZ6uNTOqRMfjGgDlYWYd6cz0n\nHOOO/g+sc29/ETg8nuFu06elPrSohjpjmG9ALosxrYZ+wA9/ICrbtL0NdyIi4xBG\nzJFVLWrg3xUQbTcDySoUzsR1rsqvs+B89cgUv66JPAlOWuVvkM6zWQ==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAx3CwcmNTmfWA9OYlkd4D/4HUqOhEsNx2frK+y/wGcrgCX6YL\n2JfSWNQbXrnr9kYgFHW2+8fhBkPrVVYxXCwadkPjYj5Sjg5fzw1p216m7M/UqzE6\nU2pbVYeZuwTtBuwAFFkEdQzdfyD/ZMl/cnysrCjOc7a+wakmHeKmal01Gz821t7L\nJiTYX+Tb26M4iW2JILNVhfVVSdMWFG+FyAd42EVT7mIq/AWOhK8dqsrABlKIYt/g\n19w/XwmFyjyd4X/pYSdysqgwvJS0kdJMgJoIA8UYpyqsCKVOm+PIBrzT6/hhS7/l\nhrpWm4+C3SffzZ6gixtD9wV29wq8g4L/320ZUwIDAQABAoIBAFY3Td+1No/IxJ2Z\n3EXpjxrmWjNGbWNzeroctURjR7pR95y3oElaoHEMCGTzxKCrAfU2SgsgBhP6SoKS\nhzYMTsgvYt22iWOlrR9J8igGC+01jv8OgeNLh/46Z2ekon6qiQJC1R1wmVP6xM0i\nGjk40kB+R3lYV7BKWhLzRvOWJX4IG1wRdjfTDhidRYTrRwWzbtrKKTMX9WPU2Pkp\nvbMJtgQiuCiVoGbY50RCqeUa9px7XKZwdGmB8iocb4jSIWxT0ZzSZWKOoZhfNBvS\nwab5Ej5VjRonOVMjnDitiOtTSVLrtBNhcXfknPEfwMIsGXOB0n3suyN29TQNL+sV\nLiYvSpECgYEA5lyDCno0rfOpzIdcuaxwyIKqfUE5zUjOXWzVw28Fa5ekcJYM56er\n84kkxL+fwkwevtqy25zH6s4TA1au6f5yN+Y2hMKoqUgxwdQqxQKQ671cjx+OyJ9F\n2brJSDzEZHqzJ/8aKZlNLa36jCg6neO8Ur9rI/7jigXHLYYREC6j+nkCgYEA3aMq\nsYGu490D5Mbaq0aKzY4PBoosTkGtnZwXXtFWVJWWcuOJAORq+L0xQCcmgwXhHYYR\ny9M+OXCG9C+Ig4fsQF7P1m0Xyh0LxfCeb0mHmE8e9jxmJ92IH9/sB8z381byu96Q\n0Si6rCkrFJlALPHMDqLTM8P1IwzWns9HcA2gfysCgYBF17qrTI34R64tosTMTqoL\nQQKr40DBKtfWn47YKCr+igWDVRsoiT+rIOOS+35WjHmt5+TejNRYLo65Lmjt7QV4\nSUYyTkqQuKDNYaRuAu8pakpL4oeJ8DBfaTBpxHLO/ByhfcjS3/X0aZFTOUc8Y7XP\naNX1HeyjBklzVfiKbXmuEQKBgQCfiUHD//spnf+df94LB34GNElwhakas4ALQT45\nTCn+PKbNECef8stlPJnk+clo6qR+IfFRbD8QDYW24zg6pW5Tb2vfhytmmr3Mv0Ts\nCluGMP2ydQt7iqTffMljXBDmoiGbRbusMssb/Y47B40ATRR7PI0WZ+leT3F1YvKj\nH8v1fQKBgQCLQMEs1ey8twa3ZOJ8L68/fwnu3ghM0aCBqt0Y/shf2v9y8nxxbRVn\nhRl6An2o1HyrgPs1NQiga6eQGi/TPiRzCFE18zSSvBjI5Qt8uWR0xnv4heAk+HxM\nt9Kovvq1yE6fedQbavH+Nj+bwRrBgkcjgAN65VuxwqJJ634HnEwYvA==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAyr47STrihyuDtDUqPG0B1tKagbg0UlfwRir0TOCRwKzHr7R6\nj1+tUoRH/ECs8npD48Ubdz9iV7wuwzdHVM3tfT0fBaTTnvxm9XS5nuIp2l8IE3OG\nHfBQJXVBxNwOyrQNy0gDMiGEMq5mvM/HK+Rrj5jCPJ0zqfNOoieCcrTnXh64/+tZ\naq2Bh0eknfVBWMv56J23kCzl5zqODq5wd6w+L3kcJBdwqnwiCtAg2GgT2q+YlEKL\nWSE3nwSzur8fnrXYR30zUfMj1gOoS0hrsGwSjk+FQQ4GEbJk4BQjnj+G3VPfG62G\neX+2qj3DWGFqaMFGa4cqvFxvdy3nkv8iF4gfLwIDAQABAoIBAFs7fUw3vkYbM7n9\nDpOAbgpwXOywqoK/ZbT9kU+i4pO0bGtJqLkp0cHHGumNZQX8NN6jDPf9QtC+MsDP\n+D5WKF9qCLKueDgb1mzv6LNxjGnix6yRGLISOM6SVFU0vUdD/M0UC7QcVvPDfSHL\n0hGTST+l+wnxp5T7ks/vPYlGlLLKDCk83OaQ69KJJxTuKcLqdI6VSoULN1n3d7be\nkzMTwVDZac173s5Y3Xhalvl49NEYd01hrDLIKhpuxEw2/oXHGT7snuMn0gl9kbn5\nck+boEyGiWCuaYGeTe4MldXEbsF149lQONaV8I40yyBKfCI94QhVF6oDssGZaJO6\nCWRbaqECgYEA/kX1dH+Qd9xrxjKI0yanSDmYJsAztYPYBnYiw5tDaOnLAdbXn0MM\nCsZg+DAYg7GZHRJK8OmOfdu9ZKRwr+LQ1HzonVhGa7DIXkW4pHLcqFCs9IHlHCd9\nKCVIVgDvN2FnD7mfKZ1Jp1TCKfxr5kfeZ3bgS40O1WtgQ7SHzQGJ2JECgYEAzB6w\nrraCrPsEEDpvzRvpEBXETc3HjMHONO+nLvpMGMse77cugZ5fI6Tbozopv3O4pnXv\no1sF4jfMsFy9BWX+mKwfDU0LvFOosGVrj26s+SpvV4ohu7UrybeWpxzoYbJqlLEO\nyWm3PlsEnGn/vPrs/7J2COKxjWVXptPy1tB0W78CgYEAl/BD2KY1NHpA6EDgI5Qw\nV2ca9OTawTcUSeAjq9DGOhWh2eqAE8mjk3ixDvzmWSMxg/6fS0+0dp5skIv2ThNx\nh9dPxF2wTr9nK3Xr/FAqbGUwxDYuKmGqVufYTpPmXahVRj87zUkQbu2lzCLqcsM1\nMYwGQP78AylTb4GbuDOYJBECgYB3WCJZpKB23zUa/+/Kbt0v6GokS4K3C8GzNcP8\nNaA0ht6JrgLlCmH6BzPcFe++3rxBp4b7bgbco19sZmymMWJQhaKKNlf8PrXMnR/s\ndvtvm54KAtuCUoVNpafSAZblOFoUoglRPhxDuxfRFDDQn63eOsvEmjbRw0UiZ53+\nd1bxJQKBgGcyoRR6idMY1VJSp2r9Re/OHaRpVvq9vVDLwhD5NdUzQTZNyG4f+HeI\n7i/mnJnNzFL9rivyshBLkZE1Qzo/DwraBp2j7nTAobVIllDWw92nDyUNZPF9gvia\niyfcKjX553BjiU0az6ysavd/NFCWWLNBqMcOsHUjkhbGNvQ6cf0e\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAsa+vh3oAkgylcUVrZHe/wjV/PenP1xQFQtC0sveHhfnZiYIr\ns7IjACWa+5cIXvxlU5RADRuFoScfnQv0EsRNpD5/5cA83zO4WkOmI1o5jb71C9hU\nOs2qRNy5lqdoooUDtD5VlzsqAojqzW3vBWSEHYPe+avSyPZm+raPYrnk/cbWu3cI\ndMIa94XHgBGX74tGjnmYGmue5NXtCUsw+KqcCNkbHjEx8qo7EQs7XcQp7TWemfCo\nbw5s3LuO91wPCWKVciq8iT4ywN/xmI2/l1Gr1KdPDE8ZGAWCo5jV3kYkRXpz4T/6\naSN6YBz8OqhOtPDHiNe2CIImioT0/JESx1sHDQIDAQABAoIBAAp7/lR1G3GCs4Ri\nfzq3YiY4d+CUWkIrmT/fBrqEpod+PZrChoec3DnW/jUYLVNFe0kaN0lOtWzT3Sas\nHOO6l4Y4dd19oVneOGODGFt/qGO3v8fKJPYvSnuztnOwPxlP1e8dunko8Yqiv5AN\ni4v66VzAejirLc3T1DG48e72Xw+mQa8KFXZihe3x/HGu6CPnln+eEIU4BDUHc+cm\nty3KfGsCPrb6WrlsJHGoReK3V8lAx67aYHeOauWZgwH0u2+St+XSp5gOJyiPYKhX\nIEhFOsxO5iUr8Ocv4+krNZ/wXHcgpJaujj/3J7E+r9OsIYeXB3FmeZJzM7HeSO+C\npZRifaECgYEA40K/uDEeeQWfW5g+6IbsKm9MuCl5rgH07uf1IqpVIcklUxbweE9I\nCKI9uxj43UXh74MAcdhv4DqrYbwnBjVTxeY9o0Bnf5ozckrFaFHufsrkJscm95OZ\nMlkUTzDgzJdm9nxrELDlkN544QdbSg49X/S9klYHCxLz+EDvb2x9bFUCgYEAyCgI\nPo62WML4EkqBd8/p0wWmix5jx2AFik3qMToIRerJ2DudB4FuYwmqgsj7l9GXi12B\nPSWOKutBMQo8SmncZoQC+9gdY3a95M8We7i9/9py00NdFkEtJrZ73g2WTft1SatT\nj3P2LDXyrqxA0uLoKi59HK9RdQhZhgw51CSkZ9kCgYBV8jtxXFoq6L5wtG4uIECW\nd8sq5ryW2zSXWW/ysTpK8bhE6Sr+RPV4FmuEI5iSjkg4/XEgnPsKcQW5i5+ykF5j\nPITZBZVew6FrOu3XWwz4NKD+LVkGTnKkgN3WaSTLGwMcesc+47eeBxTt/Ys/wYzi\nijtbaV92h69ulpg2qm/wlQKBgDmgtQb15fBWakAx7WbGmObl6JaDcGz164Jvfm8f\n60PeHrFAMKz5igO/w7UWEsIaWUyJ5mzphC49P67T6JnwMsHdXlKOXZ/Gj1Pyf+Gt\nRh8FESxcZ8/BgjN0NOOw+8aGELXeOxfBZm5CNhYuiBdvtzJ14hXBMG3GsqfSm0/G\nIQO5AoGAZUyQjxnaB3I0G8ZY4v2NIQgbsJVbRVBtC5JV0WUG9u6dL0ODvvPAns5s\nrbvoP4uov4w2s510yn8jboez14u0Y5Av5mlc87PIYVKHluMDJnhGbKqqFP30dBdA\nqmQAzxh6ol3DCWPFiSR4EUrgcGH9G/acKFfCgQBjNYY0jzGFAz8=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAxFuPQC42NExIIYyWBSwhlZlY3OLfD1deEyFfKlhy+DY/CG/I\n80+QLfg4xkMBg/i/MCZhCscYs8Fx4RWcYUEOQgHZoPjMpWSGVyoHzqFyNXCLod3w\nx+96Wdj2jLJTT32wKIqjENOijeSOpOuXhrxo5nRl809y1WzK8rmT7+9fC0lVxIia\nvain+YXmqkldk2JWauSfxfdQWBWy5Ik5F/sc44uUOVlJQA3ImkN5jJ4q2XZxXm/U\nCrqftKUYa222wkm6WF9XKL2hj59oQRFfO6O92W17zOBMIr9gkk4I09DlRGYdOCYW\naRDq8QjMyVWewNtpm5G81Uuun3wGopX86Ze3YwIDAQABAoIBAQCGLo9g63NtH6e3\nDWxDUpoMsgMQxxRpVmnaJtbyiAjg56kBaepnorpDP7oEeiuh9K0qCUANbmrKA+p/\nRxZbO2qz3rHKl9q5N026aOV25cq3DBbPYWw2JMd2eJZbJU1bJ+fmfH7cc9NEpah4\nuOoOT9hbi7VTpLozcKnrXrfiobBQc/TcckdgRzVu37/LaZd5mCJ+4UzR5qLQxxW1\npqN2OBxMAZ1XI8vKXORgkHh7iS8Ei8ePAKkTdEM4yhWkCx6UmSm7nKU5uiV02y+m\n9TlylZurER2ZuI0RqhUl28pXXfJYCLjvH/vVhvQ1N65kbEUiAKaT+hA4QK9gomB5\nq7ymSJtBAoGBAO6WetCDNVjGNw63y1ztFa5CyWOofdOByZvlHykzeByf8Xl+uGcZ\nmX2EhzUgYj+s13wDhB7Ze+3YQKh8xUiq2KJF//lqm26USL+nOdiQxLYSUtHuas+T\nsbDTi/M/NIcXGA/ApGSlnjp6FGfJYDx+ZYbua6ATnhI1OYEURPe9fWiDAoGBANKw\nGLtVnb4MafaNBBl5WADE5DfWmMedMtyIr9VXp9Par/YPnbp8DmFzJQuBrWwMLUwC\nHko8Y7nlyESo7t934ZkvUqpsY8qfAbxfWUaXB6vOUy/uk+dWD+TfMTTBecIIiyue\n4tAXOFnujisSPyAWWwQYa88PZuF/zjyGhirnoH+hAoGBAKNyoYZxLL25ZQSHMyFh\nJkYwaMkGJ9UbB+PtzjAqm3F4vTPPdULkTbuoa/wgSE54+VJzEjMUKQti58DFYV4L\n9McOj3Lt7reNjF0HkeBkJO7alLRO0guMkeGiuzCUL0cQsiIwhi6RK1nCQnJriatH\nqAQNF9wdNGAi6FcebLUPsndDAoGAMspe+/JU17b4aIZwQY/eJjlQ5yElraF43G9b\nLCw4ejsTBleXZ5h2gwmWGaMGYrDAzr0H/k92nx8E24B6LYch7KLa49jDadpROc8m\nn3zTTZistzZlk3RM2pjvrM8jgiI7IqXn+dBPNmbrAPkiHKMnB+K3QrBs1dnQpwir\nGXbDW2ECgYBrkc+VsYm5jpapdVS2/Saszzqt4NcLqgvlEgKYZAAuGJyDADk+vXy9\nJw3Q60+YST8RSqGD7O4Ix/i+1/9hCmCI/Q0I30MiPTMtP195syznrEZ2kbIP+8L5\nJ7wQQWnlMVPIjz5h58qeL+cdt3Wy2t+OwyuvL934kA6T/BcsUKzJ6Q==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzaHqV8h4q1Y7X+om9YoDYvba7IjourBJJNqRxLdQnWaNOy3j\nxYtRxSeWBqSUoeGX008LMNPY0uwv5H8kfiIcYaYwKH46FPd3NK/yGk20cP24s+7z\n2k00Dpx5FbgfI9CKRtJJzkdBE2jWrONIH1lVhsabyVPU/TvVhSg7D5NlIwaiICvv\njsEdZv2HMQ76Tup6G/+ThuRgMBbh9gcbcy75ycIb/cd8oi5du5G1855YWg238y7+\nO4fhZu/Yvh9lRFLJDd4J78wkGHxl7EMmCjbGfOjf/TAdzl0eG0wLm69YQmWF8Hmn\ndfUiiLa9KipX4C+i+oXadgSHgd57ThnI3NdI1QIDAQABAoIBAQC0KtKVeTto+5Po\nNNNPvU28Twd/yXEH6V8c4DhpwCfs0SabHFj+A1Rwxn9ncJodv/fzl8T5UhQesfxA\nWrnRnfd6A08cm4QuTKhwTFx2eXDIszU6yfl0YsJiruxUgE8gZR5ScPtjtgCpjejR\n8A2IEhCilq92VXI/MkahdZ9RxVYOubXRlJT9EQMkKBhOa1PakDxkpzDoHOL1tFBt\nZ/mdj6wUfYY8Nu99I76Yx9Ro54BllFSFPAgQAvdlR/CHT3LwHACWxyc37a/u5SSl\nQe0oAjWkjK6DYhu4P9JYoLhPKGkkvuZuhfHEod1VAliVlo6XpwEtxB9fQzaqGozq\nFcKqBxr9AoGBAO3BaGka4izZU0xXYVLD6X3QwHBjNJjEGb50KF2EJOs1fnA5H8BX\ncj1fzAB/H/GlmDQZlLhPuPdKo21CqFniPjcKdZFMNQ8k7RsOAuWY16yrD5U7jAB9\n9ED/2rqdkQLGksRuBleLZQgjA4I15wo1w6yuiMe+AIXql6OlYFUlTmFPAoGBAN1p\nd2NnLZZsdDu9Wzy0bO4YQSQYuX9tNGhbqkWo6dV+jwNTbVHaqmIv+NlfTu2JclOw\nXqxa1Um9bcqDp67zgF1NxzOCHh/U3lOnWC++Cy8QiRZBVBVHVaZ1tsgLDH6zMND5\nOOlfcFjIZj3iMDZOyH6ugtySMFcBjX41mooO9EKbAoGBAM1bBnHyFHUkaTxBJQC1\n0tEnI3BvX5irmIjWD59mafT2yXTpAjHB1hyIx82N/+taWnJ1vezMfzipMStILd0k\n+xPTZuTO6+1euaVqnrntICw1vEnrmus532TdGPGi8wNwvIgJeFTZQ7SZeYhYQmyh\nL5fRULclt5HzpHJmfyRMCr0lAoGBALBQN/40P501qCmaVHk2xDeloNn5xl2uvLkN\npb3rEKouKfxgBwHTuzKNOQEAYH3Pofb6z6k4e/hInJFT3cf3qBSkxyhBSXPXs2Oy\nMwtxrr4/0KqkQyzerVntCyypf721O/5tN+C/W1uX2bMV14anngkjNpom38+pMN98\nGCAwYFpZAoGAEXA07FV/SD4RmQi0JAC4LPOG+pIrosF9gG8ZrtoUaJRcXe+YszLN\njJk+CH6BlGOJLBZuf/HCUMJkcwtGOt+Y83F3Wqb/DR2kUMWegdfKeYEmRTkCy//H\no4/sfdI2GGpIlaLjyvURUJDFX5sjFdSD9opim74n58dM3TZk2fAQr0w=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAzwQc1OPHLv62NmMqgo7tuTgJHvHSMXJQC9mt9VdKf8o9kBmu\nge7LZgCA0NoQMiIRCFZzXhnSf1NMwVMm+2dYcIuVZvcNjLKFXVfEsI94pzZ4JvLP\nfIltMhVNWnY9Tj8rFlYBAHpNAtHvWT/qFRQ4evqxqBBeI0kpuFvqN0tD7VVa3box\nvOWUu5/6HioQGBsjKQcH5SZrF+mXGj4loHWYO2Zdc6sJQfNc++fHBs98/mgs87gb\n8M9HPG2LKE0zc2nvtNr+QCK2Al8fGx1iiagVd5G52elo4EwMZYb/syMCmjQGVDS5\n/16BPn00G1/gFUosw0bWGYX0u2b6xtdM9HJTKQIDAQABAoIBAEOy3JriQmkj8wKm\nO2imxSclJBZyjoKNF5BIh5MMENjpkmhlfaQQK4QX9ghWx6UdbuSDIQdM+oGZBCjU\nwJLGoS1s+FbLxCqW1vZHlZMH2DUWowRZs3DOoF8YiEtlD3dfQP/08C7vz+90KAnP\n1QU/r5jIEwEbGS4A5xvbMyUSAAzEndwpfinaPsVlYchuihcAbmQmvawpF58tD/W/\nXDTuiqJjcZFEFo+IoAjakCqTL5L/ybMpZHRWGh9GK9NejQiszH7VgFHVzcpv4oYZ\nxlt4ZFE8tJkulpjONZ+eZDbgq4zD+ZHObQlMeoLaCxuSVvR8hgv+7vjHaNURiR2p\nWA4Z+OECgYEA++7PO+9Il9N/HhtdhKXIDO5FKdoBUgApbEWoUL35v2w0Idd3KNlF\nSDhLTmJ0lT72q0XckoVdtY+kBdrbm8Gh0Au4IEvFog2kUz09vLqjhhOKixDiSJOI\ntnjFJvA3WD87YFAQgVVpmFaftT5TAjDa6ApSWFk6db3BSOfwFL81gSMCgYEA0lur\nrEdx4kAWdBHksNbSTkRxYr0vLpcS1diC40NAUcZVEHUNRfGf2R5Dx/1Hk62tXYXg\nmyAEjkQlyvIoS3LJfssJE5IOiKEk5u8F0VIK9w0/x2qsii93AOd0HLxdQUWq4lb9\niL6st8wA4Fx1qew6lfDMCOMY7TYYAQCbFkqeTUMCgYEAzxpDK/8KCx2nKyXN3AS4\njsC1BLGdUj6lzsb4ld+GVrFprcRFxcPOq47zgS14r1QCTUvyBlUyctXxPYrr63/j\nOHbZ11rSzeQvCOqYPPSlBsw7oRSxIH5/7ZkcYWUp6haZBhTrbJa9ZqkXQ1RHws5M\ntmH9zXkUkMP8B6X7e+Kv5kcCgYEAyHFxdAfgDKbkvlUOhTMt0kaFhICgpNRvrjVR\nAxzh/QnvpG7k5vEGT+z2yWoe+ilUOPmIvEgWDzoAiRGWM859EcSGMY/LKYXjk+nO\nsVZr/F0zmOGc50+zJblmertCYR/xZRtfGx4Vlk1dyGxR8kiWyqlYwl8jMWWKGepp\n3SPdE+ECgYBdpWez/xmL6hgYa57EVZhKYT8Bc8YtIsuYMtBjHvJmIqm7fc7eQlfd\nv7NdmWg2dIF8lSxHo070VCM6BW3GDyNweXyO9bv22tyOQzXCY2VWXNb+I3wndMyN\nj4K2jPue0g2oW3UclWFyxOPgNVcz+XQlfOseu6AYIb4uMbMwTT7Dqw==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAyDAwr36FgJICV9fnsBNeCSh/qesmPAsLInop/1HB4CRUrPP0\n5t84xbY0XBNknBx0ERaI5gmo6VGra/PBtf56Gth0+FE0BdWY0waWCyZiZ+IG2sgb\nMFfUe6Ftv1ogA5oGdwtXQHbQjBQfKhTWctKCaesU9FYxq84Zf0ukUdfQq3WnWmF3\n6D1fvC2wiZjO+wOvoe55kT8TNiNvaDAPcPAA/R3b6u7JYDeWmMob3GKarzszzgGQ\nq1c15k7U6Qz7PJ2MNPoYo0lBr42F7LSiFs5zqjQo1DJM526CCfYVs0o9Tj0STFvJ\n2Wp1xHWTdm090/ii7vOezau/4CLvHo4+yGeGlwIDAQABAoIBABMwW1pXHGDongTh\nkznTgW4LTcFx4uZagKS7ZQ8uPRFJKjyx3cXLm9uHzZkN2tUD/TQuHs0+G8zRoUbl\nxKm3TOkJ2px63Cu6/70qonNLKJ+gkNuj1sdZg+qd3LE86FiwCPKcSa7KNM9Ylmwo\nNy4mc43HlOie4n//b34+N/AIad4la1rse0Zcedq+V68l1Sty+ukGmGYfcICzAu9V\nlEyLmKd4tSv1moJsZz0UuJanFsL1LQrwE9RrvMpKyYXLOOxlmxtWTFXeuyyPq+MT\nGpfcrPSNL66Hc/XLNgI598DvEm44Adu8Q0BRJd6i4mLgFvmHrUNED0i93WNbCxnO\nVgqjO6ECgYEA+dRKFy233e4FcSyRyUL1szGbGwEQSj21NeRiQ8rvJYQ8RCeaCCQf\neTUvZ6xkxGhL1ec4pS1rxGllgmrsy2znuU71R/eTokAnlOBSaoj3WMOcV58mhuVT\nsyxWtbE/SgH4EKyF/tLoDGP8Xdh+KYEjfe0/S7iSVRP3qHrqU+62RQ8CgYEAzSID\nPRPbz0PB8bZK5rIZTOG3UK2xfWlpwcX2j9IE4jGCx2o3jsiRUrZ+hHPQoHvAVvgZ\nacz+tck3FVeLakOqwnpgw3XfzHQXRfGcQq46BByKxGxCMoTebDBZSJR4MJ3q3Hg4\nSiVh90gnqkHRAtDkBvmpycyYuBgHzbxwqIyJ9fkCgYEAzg1GsolVniijum1dLJca\nmRov6HzfF6x/mhxs46cryVCgeEIFa/OHNewk1ig80DWJXrofYTTuLogTXhaciUeg\nc5lo+lt7G5Y4TzUZJJ8lCsQDXc149EeO2jeWqLKxFXQ8SlxRBBSlqRqKWkil/cty\nH3me+/AWWS9n00DVpjY7hQcCgYEAobqNPxxnExRi1oVYrGYCWIElHHC1HSF9Bg9k\nA6QbwZIzf7GYt/Qdz06elSMSe1TWIjtdHfNHZl+MFEF/Y3qSKN5/Z9uJZRKqPrTs\n9A2VJCS0q4SG9HmZO0crPXQBBqOaxdPq3Vp30JSM4Uke1s+G2JECcl3iklIv5N8L\nG3giVVECgYAQVcOnSS/gNpZS9U6H2SxodTXBmKY/gO12EBkumEYF6Di9H7AZEYVn\np6t274ZGXPEYuiytNasZ9SdihN5rXeS2p6IyH2XHzn+PMjZSvpk3P6KYy80RiPNC\nEbONoPcLBaE2Ut6wJZuHnoNH6ZYGvjLHpc361DZZ1CNXFG3yNzAGlA==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAm67GqJs5QZPjO5oDjGIwRi1xPZPLgFCIcFCXvJJLTGRM2RNV\nwov0jsn0BvcSReXommexBcAAZX9WK5dV3kL+Ux1ZRasdBHiL2GYcc1KZar7+EvKD\nQnf+SGfXUomrdijqtLMPlRKGMyM0NIcFtquKQ/CRwBJ/XaPYEdl6PL04KzbU2Rx8\nn4izCxLf2FBEB1E5Vb5y2DwiAhqQUiVA5XyjrSIMci24S5maCvrLgGePc2de216y\nCQsao5jmq92uZ1KbU20Vi4mJe3s2clrSvCyQ6smMHcf+7NBwEgqEqIbtjqcEc274\nKjTH47zyokFgubkjny2QnV3aM2C9MuFF+w+e9QIDAQABAoIBAQCEPwBkQ49spGq3\nu4neG550Tq/OJhkNQ9vSxLNQrVknaGBbWfBU1byF+wm/TOwJ0lY3q3OVtdbgYT3a\nw7BeoLwX8GtgIg4BOUFaLstmLzlBk0KS580WjoXEDem8JFQxiGpv21eWQ2kaUYJM\nSJJS7P8D3XyCE3R8H+0wpHQQ2o+3LIZyAUSen4AI4KL/qTuRS6MLswbLN9gBXM2q\nHXuOmyrnCpj5qUWZOVCkgdGq3SpY5l8X8wfUs5UjrI1JoHWZ6u/kk4xQAmCGbKb2\nwpypFZD7B77xGj0p33LPbN+/Oj9Buxl0n9jITiZwW06OldqvMp+VrdoqwdSMykQP\nxhENvem1AoGBAM0A/KWDlgkW0NcwqISgoKDTT0AXYxpz5qnY543M6ThHUO1Hcfn2\nhENytLGcOsocFJsyKJ+k2lw2f4CayTFjlP+vC758oTrgO1vjPkvSpJrWhWzZmr6x\nAVU2A77B3mMRJ1tzDi+DHDFX27mShu8dTk2rfANK/c/XY5OQJ8JkML5LAoGBAMJo\n7kL2KqIUcafm+riPka3HxiOG2Dui8nuRwAMGl7Ug0vmPRcrNEip7lixM4wd8HeMG\nhuGd7TPftSl3tGXwGSU+XJl+dmkLN1S/O0QCx7WNEYjnCNDzZPzyej5pdzrJFhVo\nDsvX5SH1r8IA7IwVgbpRiDHACfOadte/EaKCmc+/AoGBAIpzgS2zSdAHpbG/fgBc\njz8ulxZc9SR3ZDAPeors+hF1MLWfi91aXQdEX76YGahIAK6z2HXBG2wtrjzHzNtb\nOtTXfqH+f9FIgS5Uscz5jQu5l9DkwbrJxnGrEipxumTDwAXmFY2HCbJVeOLCj/jL\nnQedqhp7OiF1gR22vPNvQPAzAoGBAL10urTg5t065SQGMdHIf+SF6i7XC0Ta4EnR\n2PhZ5WArk86GdDJVtN1XwRp890ZlC08iHjIi0HWcDhVSRaiN2kN0SZOALW7i8lIR\nZnU57FueFTeARxQyDfl/Z+gIqZvNOUs1NC4swGxe3KBIfD1r/BeRuxMYndobbrOD\nzXC8q55LAoGAVEJ3PsE9RvdjU2mGq9cZ5Z2gTKbb21sHuKNBg2r6VlodI3llJoBD\n9BPRr7FTKYBBU2T/WlB/083FL+9A6iFOoFxE4KCLQiBwoNatyDqssOFPL2Y4SOSa\nK2eo5HAkuEabkiIYPBEqLsLQTmAZl64ThrFnlSofU6TzPzLTiZtarpc=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAuEtEmm3R+W86g7lL2uqMVeFvL4Mq+jj0zT+oP5d8QrL4xzqK\nuY0hGfuo7TcvBlsjE8KyT2IAFRhHKtZf6G53nIZUOwpLvhDZZiD2CdoNh4g96r+e\nKjopvO3jUVrVVAjDj0j9ndthZWcTpVR07LdM9NwdBg4X7ZFLcCGB3GIH/liRr1Az\njMY+R1ykwAUBORjm1y0E0zCygRqxCMLu4Nyd6Zir636h8pmuIKjy7aE5Hj81vKZ5\nkDTybnVcPmHWFp/3so+DOVp1VlkOX2Ixiit+KWjYx1RR+b64x79DSDK+YDRvsJnm\nKtDYRnR3eyKq+JgQ12J9kH2RHqHgosXlpYzXMwIDAQABAoIBAFi8DO0aQYaLMq5n\n0ok2c5mKMOG8kz3lnKESrGzqAG+RAs3Sdyb+N9jt3hlRf7fcu9fw36/vLMD7bpDG\npWkhf0ADQZ0nyO5b6IEPD8gQkIEYbU+RMZN9a8lG9TOfu6V5jsd3yn1uOsf7SG9L\nafzsCQbA0Mywjn7ZfWwhBgjZvqWMp6UFY3S+tyc+Po3nTXvrpLbwjc/NXpDzTQlI\nb+XH2b3YkotEyCDzRG5ehjFyfwQzJY9uXV6GQ5nLM2/OuSlI4MWwHIDH/JptUs1n\n78e/+5UWzT9oJz7lq8cJUir9MNPhtuNnvtznYUPUdsh1fYUBrUkyKVhWEsYlGmoz\nJxX8ZKECgYEA7UH/WseTLpbRlgtBggOC7RInyBNzrNe8EmS71gs4vb3aCGCFN79I\nwEeqHpKsiqwBtF8y1rbXJKNxK1XppQDia1NQ5BGZfqELB6QEUDnKNCRg+GWJ9f/T\nGg0DwidHtCCJJFbvo+mu5F5AlBVElywiyIlaSb26po8di5qac4w9meMCgYEAxtoy\nmz3aFr1weS+HKrZmcABITMTXASX1kSDOoV0HZkUmx6mU0Ar3xjQHnHM5R5gqlpNM\nDiiCi7rZYLmXoySaUFtgMOUUIhmvb1+MRpz5nu+aCMg6T5Zq1vxv9tv7CCnClo4b\n+2ybdQNC2fiw6dffDADVvpKiX/WeJIG2G5EAjnECgYBh4/kX24A+mLugzMQagJhk\neDkxs9TmW5ydejKF/dtLbimniytr9OBvdSIbuyi1joKliSHQtV/PHiBzYN8l1VeW\nMKoPb7OFK60BWgrtjup+2QOpeTuS5BqVhQkn8k0DZOkwtS3q01/1mW3Lq3rBZNR1\nKTa3djCsyB/ImeFgC1zfQQKBgCvKIRrOpBgd34bQUJO3VxeS+Fjvf+lg0pMDBn3C\nXr7Gu6N8VKj7wVSEYKizNwntGHPQPA9iHF1o6DXEqP/kL5dCiAw8ZhKcVWXRRLLI\nHrBBU0pbzXt5RE3TiVRzXPSPPaqp4L+dSx6ZeT9G5K6aOyQJiwhMgjVfgVPIvLGV\nXOoBAoGAZhL0412yELMLdIpNDJ54/rQv0aJzoozCSHh2qm4FdT8YEkn8uQGpBcf0\nsHWhvVQ0ZrmQj2vrPoURntCuJCVSy9VA33/VIngVxq10/Nr2KypduFNdWHCUrN85\nTwAb6N4Fn33ece50HKgHzSC1dePEoZ9IiueECpKJt4eFYbsZiuQ=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAreULRdZZH4XsUF9Klp39bilYHPWytCmBape8RlTw0m4J9BpS\nKZUM4XXV/7v/S6CHNiCUS7kJkUtiz6t+J5T3WmvREzDUnh0gs69UkVDJC0FnOU6q\nKdDRpVSwb/60i2OcEAnp2yvQVeGJA0Frl3vQCfxZzGtYytsz4VooTzKAcRJz6ycr\nJ1k/OA9wY0Uesyb8Dx/xzcn/2bO44Dw1Lr90h/JIphkdoHeGB6c7O2tfMS2EJAXK\nezpwzvNRanv1AGcH7t/OUtdxht+QMPZs3Rs3w3EBKF8yol5EhtcGxIvxKXV0Qod/\nXwk2/sxMJMH0lTrXAXaK+qwsVTEY/kH8v9gHDwIDAQABAoIBAQCom/JPN+PYfelB\nkPPokK4fd3UZN16b87apXRDP/SI4VzvNdQv/h6cioTfrLCUA9ljEMdi4h2TwfXEE\nvVqoFHe2M95YRjuSR9nif/l2oSceg78LwOubef8c2L3vCb+Te/ThVEttGNF7xCAu\niOtCyWlRlV6Yp+lDYEGwmtOE3wnNF6ZBiEFFs/RFE8gJe8WlwAXKTVRLttNpMvDg\nnYV26n2FxWN6pf1MmNnO3y+UMNLYhD3NZYIWyp1rMfARyhIurp/bxSJ8wMRFlxab\nYNZDgfU1U2t2GOtYR7oxf7PmuGmSVocdLs1BOlXF1fxE/jVIbmnLTemWELI1Xjes\ngTI7WesRAoGBAOFqVXsAcm51JuW2eGUHNtiTx64PwLCE5R8O3Mr7RefdRiVenXkk\nS4ryAQdDszwGmmyuCLM6Py/gnit2wCu7aef2R6J10T+T8aXbYpGq3tduwUzy6trE\n9PpPgKodhxMvMV0xT1V7IYtW7z46re/I3C5tlEtha4uOs/0yY3Fn4DRnAoGBAMV9\nK844ztptCq0Iwhv591HKLDeSVlMttCDbDUtx1ZYHN/23Tdha/9ZcT8ZVsJNRepN5\n2+F7e4hkTD93cPuGFI6sKb7IypVNRjK+QpBQgpvWM1pMrpyYw1DoJZLOx8qucUkd\nwcGOb1njxTZh1OeqJc4vqRIDwKIKQsUIWxlPjC8ZAoGAccIqFYMqPNheovstUz8K\nzM33rb5BblQDFWN4xe99XgBrkBxpuGM6xIpRPotJ/vcOI97IgTrZ/J2M9T5eyTMx\nxWCi0jhHO6C8CQnoer8S6wH4B49oOrO/Nju9IRz5uBLPBMsH0IntadabsnoGocZq\nZ/vhJRGrJqkD6LnvOaJEoe0CgYEAqbYiceqRoFU6am5vWNlbtva1PuBLvNk2xVFZ\nQG9Hq9DyRd9DT10MJCtaBsfWoRLQ8nEzCrcx9oiymYvMFOopOl4q1Z/5r4ojfDHS\nQ/Bb/Q17PetN1pbUqe+Fcn6uZLe59KpUuj/r4uSAsnYqHxcxu9lrQmblIJsOvAGt\nP7gDcOkCgYEArd/AMG3XiGoxzRsjGrDh6TuK7+4pic/ueg70frT9KtefYtgX0pI3\nZNcYVzh8nXrRnn+ML7NVBMzduFv9T2IOiP3bAIqzzVWHNS33j0yOW4fvtBQ3EpP3\nRdjk52hLRkEOVzoUs973+vvVbiOjECSe50QO2JSlqnzoTwgS73c9eXI=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA5zvelrTdipXbcV1VXv5zdr77ORls08W769tCCV/orCrO9adU\nFiioyuvwmDycPWrOpPTlDWUVh7JOtw2qTCrdkwClcJ4UMbEEjIkvEfvwfRsNndoG\ntDQLrgCDFhNqJJX1/MRwAA0B9ywLV82U5Oa2AQEhh2ximwUBZCgpunaG3/CFnQmg\nGJfq+snfIUMITujTsW/JlzQ3IAPoUXZ8lnGzAIaCntoAi41M6hyHEQ6pRuKFVaYP\nwcsEecZq3VN3LcLWjirGlwHzzvLO5w3pgy7GAJOoi1SGSUUEKyuDmDP2gTHGS2nb\nGjduxlw0/zUO8RhdNEg3DV5cHyE6VoUQp+/RxwIDAQABAoIBAQDTuO7TYSHkrdgz\nEbmEdEl2teHDY3NQ6KFrkOQDP20Ej2HWbfLBI1OCEczHJ+ALIEu7oHxF6oNvLyWB\n2KJQgYNGAXZ6ClQFN8cpgsa1mdCWTHoIScSPpYP4O4HLadVrUF/epGLEuSROlGaC\na7tkZ5bIjqeLnwMeW9PtSBUFJVfTnGK6Riw74Yo7zhL5jYaNCbsAUgOodZvVtRk6\nl1Dp2x3d5SbxbE9G4z3ZMVESHLyB09wzQJK6NyKCAbMzqmFz9iguWB9PWnY+W0Il\njXpqW1mEV2qAnb/O2Rj6muXfsLPxub5Gl0yjnVVOkUyhTi6BsnzWB4+LaVYRn7aX\nzUzzZg+RAoGBAPU4WWQldNAFRgO8nOvSWbL7mkgbeF2FMsASl/3LLiVeRf0CZVtc\nKDNBIrW2VM7VGA6guNb3IDq9fRBLbJknHWiayeoKmeIdAdFEZQ+ZvZQhaEQTWhG8\noq3zy++WFVlQKhtrM9UsgGE8RhwBvT29JTsbLgyL67AHlxo5Fy4a3D9JAoGBAPFm\nH009hMzSm6M3xEKFdQXeCc8iVcfaFJAdhb1gwkOQhHp5WBAuCCQ76jCPh3iyFJnf\nTsfashsOfY7Zq0osAKB0qucAuGceC0BLiq9uGYV0svJFj0ddIo++Go/y2k9ZY6Vl\nigXmKf8iClfuOubAejTUHu7j7Kca31L9uCFSDLiPAoGAA84pDPXL10KNsJcntD0i\naTWgUkgi2QitHe/r7eNORwQsf92epwlKEmmLWViH/mGuKuPxmlxhYqf4Hd2dxM6n\nlSz5vykE9E5Q7edqzhArsfXGwW+vcSEFQT/MRV6F3EnzLVO/jSpQn7GowNyCDEg4\nIK+uSkGNmcC0ZpLGOk/uZQkCgYEAske6nF/vwRlVyoA4GOzZVUD7u5dfBN9ByoGT\nMR7RufZ0cMNHT4O/jYsWVVDbOz9BybZLR3xzh1n3TtXhFEFVISuowDwe9EETDCP+\nQ7ura42ZTyYiQBE/oitDEMbJhkq9Nfy8p8ipgXyRaQlPRPYoCvYR9losf+lvv6oU\nH7+qF28CgYBAkeguE+RcJi04/Y4wzNbKRYxyHWMfZp/kuyRzDnqxZRQB+jxr8uMC\nq64Ze0xspR5EQUeSjgy+ARCrnSkPU+CuOmwEiPyGL7Z2ljrGn2CqG+aWMmV3m66G\nAh3GXq8C/XuQJrkIizbiFnXSzckIFngRnoUGU51m2DOvTrcvVqDahQ==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAqd7JPH/uEJ2vB14lbKzUDzoV1ZNv9L6PvPfi2iCBoh2iukZz\n8inGdTysAWn9wSOKkhEk5+O//ph+TIhO4aYuQrO7RQ/nQCREOOYMx2Si4duTynPe\n0JJXlytyNFSCcvTqEAEJdtu58vycnPH3YcP8rJ0gzAXy8S26gKRLA5ZX9JKSXZkE\nspuR9nefZEJ6tbs2KhmNBNM2iDEbvEs4i/MsGq4ZlniOwHItWdm/gyYw5Yr9B2gk\nxGMrtT4RqNOkTKaF204LtAAsQ3fzZCKWJtajy3OWQvj9l3f33ATXhVQ8UD4HJgY+\nesB0u57W+VjPYCJv0wfoeAhW9IGIjfsKZCbZmQIDAQABAoIBAQCbXZXGzOdLsT0V\nywkFtaka6DByMDB0tBo5ZqvIf3SxdtOP9cI0WfsW3VAg1vVtQfmKkT2lDgxbatQR\nYZZsEhc7kLGWf+4tHz56oAZOFIBZfQBWadYAA+ky7Vy9+hF53QF7c8dZO6x+Todb\nkH+Hnr6qjegiUsqZxg5PfHTTqh12JV6bSx5gRhOXohz5NevPhnidupuI3HQg4zPw\n2Cic56xXL3U/XDw4nH32nEGhU1t7x4eYJweJ//hktWPKr9D6mTFUcx5NHg2hLzBF\nEanRsDekyVtx2QnFjDzh649XDFNj61fWCNAOuPPbeR79BzR/BJU/nuO5qrkG/GBH\n0aHpj0UBAoGBAN4Un3QM8jGvxr2pZgaBu6ZIuJT5XX//zT+MANsQmrWiVckYGaHC\nDAHetFmA5UcIkdr232qCaR8geqcNRej4jH2h13Whq31t2Z2QhNgKm/OgFP/iEdbS\nlEGT+sXPvkLKuhPa5dAUd6cPFW7zx8M1TDoZu+Vs9lfzqdjCexOwtdThAoGBAMPQ\nu+u2jzwT1+Be0w28DdfcV6ylF2qmnRPgUoZbpnXnFcs41ssulHjdSz7w0efuohZB\nyqZTUY2hh6bt1EvjFL2nA2gCYcHUiwMNRwXteu3agCjirOgwmt/1ygJkMoaUI9gX\nOFbTi8Fb94mFk5HCUvTaTkyyO/9CwvnjrF+QDWO5AoGAQWhFhkKB2oxwtk98ExKG\nl/T3GDlRhm9qnfq4Dc0YI8LKDU4hznh+XNagu3OxE3i8rIblSlNabqZ8OVM5ceIy\nTpYqBSU2gtylqTh1R8PoJprRVIPayTCAJchVpYkH+lgG8e4YnW5Jx1Qz9deq0B2U\nc9A8fNfYvhKvxX2SDjyPcUECgYBbWEgRkTwIHomZ1SJHSe57QAJ2Ox1NVyP3XbGh\nk6d3YLE99+nbJoOETlcHqXeB5kQeddp+9Gca1rtdF5ztOaU3Q23HWzUXInsmlU8I\naNvrkP4wFG3scqAM0a/Gpc8cEIcYCtzxRAXx0WEL+g/48tmX9y6HgK2USiSCRb7n\nZV+ZoQKBgHUkad5Yto6nMeZI1TCA7cg1nm/z5RKzQdxQNp43OwfMlzePawOnbyBU\npcisGbQdyFQrXcQoDDDpHSd695wB65VRkB87aKs4S8dPgz6uHOqgKmgRjfsXw8gN\nifb0jzpZSET/hVLBLBaUYkguMUy1bIcIn/x6pt0X9v+or1YER+7t\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAmjaHl5gketcxwUe3xHJ1rxfAhaLdW0hpdCGsg+gNNf75R7Hc\nEllfWbFnNVB6epsGdb+C4sYWSOQIKJtnpPgxnJPGW7wrDWjhF/i0vxpyqV/IwNW+\n69Xyd7gqq+TID3JOl1x/jubB1D/HP5pF1HWoJfpeush0ggL/YyJ/GGqe9b739iHv\na9G0JbXM/Nw3YZ13g8n2YawucILpykoF7FLslBImwdvRLRt2FsFUU2dXN/ppRSmf\neITjrYl94FF+GYQkYCzDNtrfTJ8Rft5P0tJFxagySIn/22Kx/PSuryIbfEDC7t2j\nUJoagvX58v2HACGkwssv7Ol0kmztS8d/lBwIVwIDAQABAoIBAB7piVqQ69cX5ehZ\niPO/AeocN8JR+E9A/6qAyYUTcfp95thBRd0Dv0JKdmbQgAytoKjfv8vXa0YnN590\nL115RvCdCqj+2h5xBJX6i7i3A38CLUrCTegWVek7qIoWbSfzgN+EciliBj6WnkkY\nkqDZCOC8BFsqrXgWQ71Kf/GUBdM6qe95F5iu0jl1OpRyzuQ/1eBq15Sse/0nyvMU\n9iL+TWFSggMND5JpnxVlIFlKsW/RRp1m/YeU/R81RGqR70Miku5ntEDt8hGP1Ut1\nxVBnXtuJm+cJY2qZBG5/LXI8BstBupPC1ia66aen9ySZWuaA5RJnuDNAKUWZFjLX\n74BISSkCgYEAxwjxpjBjjruW2ze7/f+lGS7tzB1LyJmg8+eCpXb7d0hBMU/edbEu\nLAdSppt9BFQaCYptn3Gm7wS6M1XjvzlDRAWiKwqd1y1+CFlKcud2yYjEaiKf1DGZ\n3llFzn0kIt0u9yLAo9w2G68eqPBl0+dXXJXx8UD8FQNG3+UH10cSrQUCgYEAxlmJ\nFjNB0K4LJRrcYirxwoxEUqdKgEVMyS9sNm0IGZ4Np3KXpJIFdoJ6qwVlBNXBB8+D\n4QgLrYc55F+021NYtSbKiamEHsFrEcYXHKhjCoiHbLPpEgnEytc703Faun3QiZwv\n0u94CjxbYiXLuFJ0vhW4v48PXikqncMTP+yyfqsCgYBpJYJzp1CREOzrNpBr6OgR\nW8doc3mbqf1rnLxaKXdDVrqKEIWXa1jXDa9OMQpkhQGUVLsA5oun7blocVF1rgyp\nHMjFMECecnwIcRNxjHImmcgZwdzpvqJo4oSMR3QVrv+ms4dNiYJUMbliyk0hGy/9\ny4EbUJdbinWgxl/UDMoC+QKBgQCjSAWf7uYZIY0vKGOPtIZ8fveniuu7mFDTtWRB\nkZP/cqsO0zieU06H9nWA39n/Fu/mvB92Wicy3IT9yQObIYt+5MYKeS+4GcVIRme0\nQbH2c9n31ErjfIMx7+jh8QidSQEopxh/bn7E/lbD/xUrUUWlTUBfASfHfeAFKxdn\nojR2LQKBgDjT2Tt9DsRBKKjgwF+RgnaMtAlV4TYweagTt0+oT2rn34ug8CwpSNiz\nWlFHNMzG0ND08/wwu9C/8e1COTTjgQ7pHNB3ZfV9ry3QT8S0gpjcbDyaNCzCk/ng\nq/b23pQMi3gOFsCgnUkS3Ug28ROc3PRtcZ52mNNKLjAAAwMBgyc+\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA2rHEjebYBKKwFbET7apqokpsiZEcbiwYk2frBgxqfG5fZQG7\nd9stCoNlK7pGFI6nsPMx0tgaLnUKUkHmkxLr7zHQbn2iR+HtGwri7cDViG1M/adu\nMcvWPCzQw1A5rdTMdLqUZFKFhKcukMXxIn0z6NlfhFh8UaoJXCUAFk0Z66TevaNS\nGh0qEkM1GHC03kDh4Zw0eTutCKxlaiKkAj3aTu9MF2ww1CRQyhPOSyECx8rAnjxQ\nMuTI3FN7ATHA64zmW7oWN24ktFm8MEXNKAujx8Mln6x+/4EiI7LEI+Zpvsq8tdR5\nXUYpCDTPo9uae/rGAp6vhDc+7OZpBGOax0jgyQIDAQABAoIBAQC/lpkBT8jTVss4\nVaeFwJQmlVi8s9naUts81/83j00CsZb04C2h9OtimHXbdxbKD6etB4EQm61kIbjn\nE0CyfBMhu9xDz+CJZM3ZsRHZA+SN3mNinHmXOmH22bQSR4ggV5q+cG5r7Td3XRcY\nUbDMbC87wKNDSFmxGWuVw/xtjSI2It05qc9E0GiF/Pvc5XKz9ckwBjkGoWDBiw5q\n349f2X9WYjbMQ3d6aeKVJjTGeE9UzAoXFkMhLZ0gnkbrznt+JdUKFGV5eBkA6Bvy\nr760gXG5ojFydw8+0DyIyIWRQ+trjlqE5/4aFSY7mi3EbwnDqUUyPz4QzJoPPSXY\nLy7gn6cBAoGBAO/sHZhTGiVozMF7OFG64c0YprL0lbZ51JOE8cGnAMKZsHQJ5yCy\nLMoJjcDtHIpYjxZn1qcvI2ryiik4gTdx3AfgmRVVIV3ufXAHeZfMvGKESBnZ4GSW\nIGLzgfyZBWDz2ruGrp83BpDqGgYOGCYYW0jT12Xz2D0pnaQBAR8qNVTZAoGBAOlZ\nfFcS269syfmu46c36mNeVTb692Ji83cD9QJ/lU9a2VHbe0E1DbfSSwbBmK7qqBmZ\nKK4b8hnDY7aiOKsp3yfNdaJf/NhapaxAt17ZfDnNuRTdocpASZu8Y8KjgxDNmtu0\n2TM8m7+HyUqhTu0mOqKTpPrI8SMdnXJ0zd1qKbVxAoGBALx7xGxMrPfCs0k1InOi\n242i1iM03wDyCTyzbrHyHzjCQMuIlBktoN5ATyrHnGpbVF4Hyayh7bMxQ7VKfMkw\nG+ncWqdVr9b5a3Y9iRrmbwD+Zx5X+l9LSbozD2WAWadnK+myzzCpq/Dg2Pz0yafz\n7pLySgAAZ1r1fLDBUT5IjIlRAoGAFP1WoOJyXlIzzMC9e4IdFDkdAygJ7pCmA7OY\na1QckxnB6IoMiS4DgOBqnT32xO0Yl8zeCuM6oLShmID5BfRrPUJO7X8i2Dc3Jcpg\ndCit8ukdV9IjxnSH6nlEo/JfTLih0FdxHhm6jLzjD1J5Lf29UnhjY6eFgTodunjl\nN6BgBhECgYAnKeuBtZVkgGNaXPc3yF7ziWTBChLywZ9uMPeoqSJMvlraKhKVz8Ju\noR7tj7mtMq8cPbn2GZ8G+6pofNOLO+jNMQStge4OgjznKFY+xinGYjhUHqu4dxSX\n9LP1V21sACMw9Bh/k0XXZngsGLPPzCT3/yEuIdZgOHclazWlP9wMXg==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAv1rX/Ywnz0x2e55vywBF2uNNx7PTI8uCdz920fJDPueGevC+\nXlE51uJLnJ/EHOLZkIF5gLFUhCqciNKzOkR00bt+GoG1WBMT557/jK9XG365QZCc\nosM+ZwAQr3IpZHzZVhI9Fd6R0DHLUz91h/9dE7f3CAP6pWmCw66basL38Al/7KqB\ndEHuFzY32tC344IxyKMgseE3tiqbfnUoMs7N4+Y1cckzkdPrwgf3jt1jmGZQ8kds\nAxXZ9BrJ2piSIRTIQlyF9CdEuvImA/wzDyiXWCOKEj+7ujasOdx8AThiz76WTQ84\nAQRVOaPVXaN9XeJLcKOv31IvjMeWJQuOwsxzMwIDAQABAoIBAFmKneWETyCezBae\nlfttHkMF+GCJkjR+x4cbiHNHpRXray9eNgaWhbU64VziGffWYy5Ol9eTo1PRSLxx\ns/KBFBUMYj7vETesfbT13ukrJ1yzWOcIT3OOX0X9btFaX2opTd4L3M+9/YdptZY3\n7Lx9z0Up4Yu7qO78R3df3lP/9ds5Fhs5jcA5K7Nr3DyZoPTsu/jZZ5FxMkbUMP9e\nPhyqAzC8QLSt4yIHJfK2ApzTgrRAsKECptAb2tiKUd/xARRwlcAuheeZ+DC2JVvV\ngPRnSHtisYDCg11FpW3gUISLHd29t+v6WPynJ0Xitkfnekw5bOgZzzmOz6VUOSMH\ny0213MkCgYEA+eoMHxiRJnE1lIbGbmmn3vUYDMTYYI5fuKhsjUsiltk/aBZmmF4Z\nfEgcutm7jaBVtGox5Pk2sVrKMlt6sQCZ4hxyBAuI2dtQeIedb58P/o08Ku1sqwoS\nEbKTFXLEDQtOPI/62aWtlhuAkW0gjf20FhJxA6vgGBdSLrMkITqXUwUCgYEAxAO9\neKxsEf8tOeJafunfqJpQ/8i7cU30V6ZMnC7WC/YHaklfyUpl+0ssA0sOdM/KyJwx\nAnyjn3ejf36gDcZz2hiOsKNkYJ3qVfXBeARRBShFkYTF8QbiW8YwZVbAx7k0zxf1\niEd0EH26UPcVSS7hjvfZRie5o2yT5PwCcDc/8tcCgYEAvSA95Bp3Xhw2bBxCdrRG\nchgRBzjAdvHHxrrOy26c0oO91Et/gLdVmhQIie7UXK2Frm7fK2Td2wHWGp3YD7Ng\nwRi9wAnqmtO55jzbC+nX9M91RRdAMYABhJsbbBPaAfUYVASx1Zvn7b0KYwOuq0Xf\nzwRNunWLtS+Q5f5zzSHF+vECgYBFqf6g8MbH7qUoOWZoTL6Msauv/KuBrW7158i6\niXrRbXNdNw6v/50XHrRZx0cwBNnRSpGwjJOqjV6yZVKFEs8B6FBuPuPh4r3yJxHH\nkeEpsBJpkYK4EvZIH7v7iEWjthMqtx0wfhSl98lMFUSfaeYIZdX2tQ+pIWG+tEI5\nl6lGqQKBgFinRxfJl/JqTq0c7jQsT11l0pyVatZ+wLH7cecI9Ol2sZ5dOuBua0Wh\nRNR8eTYvdmTF96mCogM/baMuWmTh08cXDsJ5YZmsKt5gH6+Yr0de0xY5qr+n11wi\n0kZelMLReOknIjsZ9sa+A3CpEwkzB0s+FM767qllF6JmVyYP8rjv\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAvi7sfDRTi07+YKsY0tqDaZ4/vCJr8qD6L3Q1HwJUBGUlvX+a\n8GXbgAxA4kgL/MRRRsfc0OpUenVfQG1lYyro0PEOePoyY+zg7DQ1qdJMg1F2c1sS\nzfBljCkD7/RlKmZweduYqDxfo6NrgO3hkD9XxIiXOS8r0YLMTZ112xZ72iRXq1yQ\nAwTTP40bZ2NVvaP7NnqORbWe68mNDBXzkGv5QmtDz2KIS6avHM6khX+R17x3Nu6c\n/kMNmTrKIiGR0QR1rVqTpEGGL5EGDtU+Sck7RyMPcEyALSYMuup7RQvboQRlP2ib\naJmInAGgvCQmUd1kiEGPRLFdPWZ6UzyFAKrdmwIDAQABAoIBAFO+Yc/HUspS/Fwg\nuwuNwLPvRO1Y0uPB0qi2qPWAlvJgf5/T4qvcjizyOFfrlau13nwmYat0o8JQkKLU\nCQMD0ATBQLgwo6OwmNIpoz+mJJ7mbhwZjEw2gOCY5uzwVuO2u+xMsAnUm+B4g0wA\nc4SK/+DArKY3iTn2OHQs08qnOd4xw0VvFRwsnJpkkYFgjBj4njTiJY+y6gdIKITo\nJPYjpHQTZDEM2xuKkqOczZHPDwzX6bietPLaTD4MoRpZNWOO9885I26xEfopJKjk\nPH7+CqN2mtOHBRNOILsm++pdgeHt/Mp/ip1RcT/bYc6qYr+EYyB6Mz2+h+9BJ/Y7\nwUzsWIECgYEA89v9woA2Gp/MCrhpvsKC0h755cbKoa3PBTDTA3UT0Cb8gATWrD4D\nzikyZDOOe7KdBI9AAqtLdxb1YEQoWozCTsu0MZPpH5b3E5RcROf+8GTM4di8mu4Y\ny5DzWI8/RzuRKzrVhJsTrOUqb82dhRbZBrlqbHbWuRTVCiqHOrBF7SECgYEAx6bT\nnJE3bYfhdoqDu+gM7JP+0WR+7MIMfMaoNe/e+iV+aTJGWEeRHgksASuzuhUX3fRF\nSjR1HvW6iUwdBYvu4FZdz/EdZwvloKJQMMwYhT37JB3aXwrlsIBsxgyZLcN3Y74r\n0gt1ucenifqJGeUiro/lxpBPFcxV1PBzXW8XVzsCgYEA8gOFX0/A4kFuzcDhaWk/\nTetiFTFqDnUhfMRM1ySNSko205J5vq05N5RZJ2qnYIoOHOVSIJQBrCoD0csyxVLm\n0C6IIZ/qf+hiQ3M880HPGKLhVs6zt4rO3tB3QY/AvCAY3W9hUl5Zc4Bn0lecmeXn\n8y12ujhit4I+P/2fAZbJfyECgYEAidcdHGiKCx4YRJ5nSxFv8tpOwMSQeOeUdYU2\nV17P5IaEWx6xwHtQrpsIfZptkJjFjKD8Qbor7n8bFXWuR8kP2GToigtfEGj/VcMa\neEZA26UqQgo2XQ4J/Kz97FKAYz8jq4J9gJwW4Hf+rN2DUJSJOFny+v9hCpyz2btU\n+voa9EcCgYEAj/y9uvs2PKyTh491kxus/nbiQFyYMyWVv7dJCBZC8HjWN56b7nlY\ntXpVbAlRUJyfUo69x70L1t600BRhxmiS3sOQdMx8kRVPQHamWwONHkOVvqenodXA\nyaojXHK1v47Y4KrfV/hoQqQH05xIcnPMiuLkwL7dd2qM5q0DInkv3u4=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA0YhYPOyebzjqxQ9uypoHc/ZNEyUS3kputmjdaFt9L8rwtxrs\nAwYm/pHjSuL/5Y8Fwtya1wbRPOkhmjXsSycO4fc4LPGqiS483Ofdzs6mBNPk9xX+\ngtkkjdHliJAhVuHmXD1YCCCeIa++bzw0hO3JMqJYFtT1thy/F0mumeYix3kOv+vY\nZ3VuHBRDdI4HKOnIiknMkO+3u+6HIHxO8eaC6+iasJGnsH4KVviLAk3a0YBBx93H\n3nazO6qWnmpomBMTiYK9JPgLyC4wDqRDsD3DDNfd4WpO7R+RogXJCwbIw4leGJqh\nJBJm/kFZjmB4dbloKDVOi63EAwbAJastFQeMLwIDAQABAoIBAAZ/9UTGjk+i811z\nkhtE1fBZ9qAeaUxuAKs165BeLi76W6dCdYnOeg0xl1xuWEPVwPb/guoiswDxtnIj\nNBZMuuB1m8+BG6ovvgGYS8ArlujVbefgTcnfVIv1s+odlnssfMnluUJfSKiA3CZ1\nNqfc/H3OnW7v4kNo9uCRD5KEmhWX9F5SeuiBIoTjJSlTEBKOKGoldglhPFW+rEnN\n+m9OkPL+AwIwi4QGQQ9NX+nK3l8ZSM1a/EMk5egCk5JPNgDBVLYVHEXEhwYAtAIZ\nyFFcGWDBik2EqX/lIX7PzB43DhwA0iilumdGdRamCRw1+cT+okTDsat8DIQdp0i9\nbMeas/ECgYEA+LPwfhEuP1Witg/3o7KmLQ8oXtHqgJ60uO+D5FU2nDqVXMx4MvRw\nYHDAZak/pWtLi6YRdD7IjwvnOciA6De6dAVyEf9DLqWBw1htxAmZOD9Jxn4oqCOX\nV2ZV5tpxni1mCXlHRrDkh/yNV/lcTDHAFPTmyBUxpgosNLQmcA9P3tcCgYEA164w\nVk84kqD2BDRUu6fZNPEiqjV6nFuuqDqBxHwSt8d+DiQfygnGwkIWf1x7nUoK6vBW\nYrfWTeDf/+tYpslzAg+cBRI2t0rAUanF3xUGKj0h6q2OFYdX8zhgTzymQDzeLceC\nUOEgekFnec6NTgQ02mQLGiWtmr2F3+NDUbudSmkCgYBSwwaH3U3Hk8bW0U92cS/t\nlLq5ex+Pd8DqPgJlDJpkuPYVcJGbW06+OSc9bWoZsS/to/Dw/yecurhSuU90d38b\ntugz5v9DWZ5qFFZKw2ryldUGMdtzwIxPcGWrsgvXOIJZKru2YY1bV4btvLSjQncK\nQkk+Uhc4ivAVBdI4vk+09wKBgQC2h8yQvur57Q9H76HmTt8cVxcnRatGuLDP3vQD\n/Z05u7OK9DeTHUMBFD1F/FcBfrBOtKfnU/4iWrY4OE0Gmmaiz+MSx59W8ARjGHNF\noN1e0e3qVodw4OImDQg1BvcMVncR907qdBywWrnf9FbSN+0jBpf+wPRziQHLwOIw\nQb+UQQKBgDKoNuzF3Po2nEcHJmzqVUvWHOYXasetze7/p8Ex/gVTwCDJzaHrdqu+\nUNcFq9XizVecpsquYGACaxtbAakjD8Nf9nZKecB7hGkWE030agexjkartvJgj9GT\nJj4yJdi1suN+fZU7OwFoR1mrLy5Y5BTMA3MYs/JEDL4ZHh6/XfSg\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAt2tTgNu4uZyC5xresm696IL+rZJVWPTXue/KAcKmfWx2b1dX\nVgQhUVOmh+RNfBTckMLQK/xCe0ZRa96abu43G0+BvKKjMzppksn3fL6o9vOcd45D\ninFeW0qCi+Af43fhGOEE+8iHUvf99nsYkJiK4Qxrvy5IG5/N2DkiLwiHn2/PYPvu\n0ms4FdcUkUIVYVirc/QIs4qHbYKQ106cOacwlWje+PTwuohstccp7eUB5LqxGPtA\nFhSPxXLmUku13B1dzvQr27CP9jk53MDF8Lxwfl3nyva4uZ5lT6J/5vGhmgEumUG7\nzKab828feFFumDdOeceECCFsKN26FVqWgkdx4wIDAQABAoIBAQCbviZJZEiWUq9U\nTRyLpjzbEV/vQjH2NvB2kYWVuRJlrZNjN2aRQzCHPeBJwzGICq6+vKzGrIxG4I6C\naaH4T427QueGpRKujQrX+WaMJdiS2IgjfEkdZOYZ3kq7OrPZEhGwbmEs6jj92lVq\n1VsmbGCAd2uGCmZwDmFbGChOh4+hcH1RZVxVRxchH/P75APtVB7VH/X4aG7HcoaI\nmZj1mz1ssXMRzVLm204R/HvicXUcgoo2Pwogz2jU0d8+6CckOu+X2KnDDCNjTIeG\nxkEiviYUmLlWnt+CfT88MoUJEP+MTFuRnRAo0Pwu2jp93M1yJADjFOsn/CI6DoOz\nFsXBIHIBAoGBAPBBOID7um1kSlnIAADiAxLMphmCwrlwGe1+hFv1o4teBycw9qh9\nQY1Uafm+R8IeUBOWt9r+HwcYRikkQn4oaBDWeGeypBX8nT+JByx+VFxiGWHoW1JI\nMJTg3C6aDKe6EDlhMzhYN5r5zsZgHz1wMgPYLW/NgGwB17exWzz6Sfv7AoGBAMNw\nkecqSmxLIcHfFQjJtme8p9RJOZDTAYapwWlEgpnYNwcc7qf7HR2W5njpxlwfXs61\n5Et6xHff/KfnfOXLsa0pBm6A1T4i5wFzmzzPTM3N9PcFFxpUJh1OmdqrJ8UvhxOz\ndBS5nhadqd+uieC12hI3gnWybNdF3FvjNpY4LFU5AoGASgxgBOkRCXw6qjdJQPxn\n5BhtLTmvGIZjh15SHyGzWVYOsFaX/1AzrwKQcyfhaAP9vDvcyMM//ujNXwrM76M2\nh9ICZDub9SbZtGZgbqc1CxA1MIbz9oVHa+tuEXLraZqgO38o0TnnUX2i7GNd3YrB\n74tmXaoh5agfu38PVtjxipcCgYEAmSEZYGs1VrZjY7i7jWAmiA873EvnS77YQj+9\nqBIi1hGfw/X6VCmfvXBfcjuQzNtTeFjx3+X4DAi2uIPay14W/5rWTDyrV/0WzH9d\nEX9HKTWHCwi0+k8Wcf4E9F0IUyqTBm+jsx1gbU3pLLZfl25Ex+MwV7GOL/mv11sm\niPrIWZkCgYBpODJMPXSGAK9+xJrT1fPVwIlGWsGMKN6WZszjAnmrfES6fKafZMK3\nXc5rANH9g0Ta7bhTfob/FwzfF7JcBUqxOMLKV3mOCLxDmpBwqbD72z01QQo8+vWB\nlgPXl63SBjg3gswO39yFe2jvVRsyCXSmBGprm4gUHg4wRpgAsW9ULg==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAzItyy9oirOwClzCjzD9BRK/aYbtqDPnf9iRF0eO6ZkwwP7WT\n0YRBikZLhLQrCMwSp4dr2tgX5RdkwNW4+rpQphIuCL4Vli4QmpzNmQf+Z4mkUXp4\nVSfDGkEytOdKniykgsF1E4bt4qu8WiTMn/4kiMWS1jMM5hWjhzIusc2UEpq/2iY/\n0vFV8ZgXnlfj3jIIIt69OjYDZuPMRnC0DbUsmW6Cwl6o7yJkZ71TXV0adQoGdXGQ\n0wtB9BpodYpY1xeYPIL1Ur3yznz84GYx9dGZNEMoaAI6lhjaSrM8TlZGP/vy6xNV\njTjzxhquDjk4IbccQlIlovnCkyxW1jiNObPwFQIDAQABAoIBAB9kP2f9wFidmAAw\nHNNZ2eB6xJ8/LhVzu+WAx55gvti4VKdSbK85Bv37r9db90bQWwSPCgR6KaClKHdX\n1Z3VPk9eduYslg+IXk/8RKl5ypeFTkya6SDYi17aJrgQzyjbXRwcKKWojZW+GZgU\nEbt0O5MqD1EEYLRnGcPD9dqf3FvGUfI3mQy8pj8qm4ggglbe8YK5v+HMpNy7x5OM\nSzFky7fpXj1KCWIeUuYgDMe6GhXmzJls0h0cPRMLIORyEDY4yInWBzkeFqq3c9XB\nm9vyuSAiyGDGBTPYi7/udQ2ivEIo2D7rXgYxZkQ1nXTykn1Qh8e0480gSYGGk9mF\n4NKvE0ECgYEA5XwxMad+4OzKFeyBY+Td7v5IFyxOUGS2mrlo+PiIOUBtJWSsawXE\nwUEH11EniqpH4256n0P055ps4mOWHY81IlD2sGM9pQ/XXSRhRdU8bbZ8sCpjljiy\nsPuK9wy/b0A7tBZZld/DiSPs8RB/Q/1eYXlTMvYIX4zLPNmn+411IL0CgYEA5C2O\n9YX2obfDmrIXZD8jHj3oLr68G6I0agV5YdrJwoMTbpshmrT+AyVpgvn2ftc1Hi9/\n45zfoCizyWCG0g2s1qydf8CVo2WcjIM5QyKAk7jtzlT5jvz96zF5fUzLLDDYkU17\nQ7xWZ2K0FZoa5AIPzmCfxMJSFvpdGI8GTYxHnjkCgYAygPV2nw0ORS5oSsdjC6BI\nE5AcAXUuvittu7Y9bLzWFLawjwpe90MI4N3v5f1UyArQI6U63cLNIbAq1o3X7Ydd\nCQJxaTXM2MKlzfbWXopQ/uNMBdgq3El/J6y6mASdYD6DcJPApyEqUky39NB9Twpx\nWzkTMxA+rwafWqtcchI/5QKBgHD8rkGdqFbD7DP54+Nb8EPVW9dp0zPLyjCFrWlr\nMWUECudGIgm9fdqI+ApHRGvinQbpdmZ7E7D29q/NAHWbF+7lYsgT0QUsF6zsfbpk\np05+0CLPqRPI6uM/iG0nTaQAO/0XZOZRqGdhVNMtkAs/ihwjdHm1z3hZWSgECpbz\n07LhAoGAEjfjjsW5b2iEXFZHSctLLAeEiSO+eF7g6gY0dIpCZhSD4XMEtGFzJ6Bd\nU8wdMXDzcmh84dxc/C0sSqmb6uc3RRRU9ZjH4hF6LkW4ujc7x21IeFYbo2EZ8sLA\n0ofeLplDRSm3UlECJrvEDAp7KUy9EOsiU8UKqe9i7GwGrBL1zoc=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAvOgAcSSqrVOzxJs5gUd3E/oATKwZDyA/SOLrDr4+DXPCxQoK\ni2VRw+IXG+bmTVn5emBP0HibvvvcmI25oeXeKU1rxCDkTuDUzb+hf9cSyp8w/HGw\nZoLdIQmq4MCeME494aS+7k0QZyJXVJh0ahmavufPBJvAHjnXWmICHU944SnHzPK7\nUG1Vh/KpJio90GU+xmhnJPjnhUugSEfKGdxSY1ZmHZPK8NB9JQ83WJa43ObpJeWd\nw6J87bCjD+c0rcYSV2qdYHG61QbIAoaKIg7X24d6s/jZ5NOeJ1mtmdI4DULx9fAK\n+fumGIWaD1NUCi2dqlDWp1H/A7skB7+sNOmCKQIDAQABAoIBAH4Ly6ezEJNgg1XP\nQRqK5eCVcre2KW5XVQK8Lp2CwQLW2AIDhi+m5lzusf6zHb5FJ9rJAtiWcU2I3pOI\nFXH6YDds6lhOnignirbSg986UdaTS+tVrJ9p+ESAH92hiKuwuUejVqbJEwXN+zl+\nFEdrWD7Di/WGOzWv4Ir1VHXcPkr2IFiyghb1jNv/hGi8ARMp4rNMBtuieCBWKnHR\nSMO2LMC2FgGTLmrI73VQO2fgGrLd295bzNzG0VZ/qwhWplcq6gKM0uiRhd1alKGt\nSyT8r0pL+JvuUgYmSJaQbGwRqk6Nv7ORB4N51uT8pnO8D582qhTpqfvNckrDPF/j\n3hOK5wECgYEA7vPGjaYalnYH8kiB+Y84+CvwWxl6n99KFcm1XSRGm/38ExuVoxdb\nhSZVXY+o3G6/ZyguWoHgwvhS9kOlkIRJ3PQkKjI0ikRsIS0UOMsQ6zIlHLA7xXNo\nDWOmyon2RJjGLnkBohJBbK9uKl/rkmWbtucZMKlh62mQ0Bc4la1FI+ECgYEAymIv\nnzYeYxpARwll0nb6v+5xmEMB0fY7FWXl39fJfxFzaUv+wLord6Ouw7I6sc/FPvVy\nHfTE/41CjJf63Aq1PU/Mkt4IjryjF/WmslfIgL6mss7XESxzGHdyfwuVx6b/VccZ\np5Jk3Ic22nhX2q4I2JqIKw/ZaI1FbqVXHNsbJ0kCgYBrpI+WcfRaQXOU9orutFFS\nouCU+WZfLMSACizUd6oDTahHp06CeNw2oC0mh7fnDVDWFKjFBusMibc9zv1/m8h2\na9j61UEL5ITdcSRB7WCCw0Uqpk1WxoViOrkBVqMFBdmJR+Ovcg/c+S5kkm9ZtMIv\n4+1aGYPN+/FLfJp1+udBoQKBgEG8aKl3I6Ge+jfYppkZClNjOMRzJ1kyeHh305XE\nem9qlKkkBQhGsC25Hd/0uTneObw8byepxH8ZO/98sf/c/od+rv0ZX4A1Z2g140JU\nXwzqYCz+ID80x+GvkqIPekQSSydqdzvFwjsbNEb8BdVC6B6q+wW5rS1XjbDlxYja\n5lP5AoGBAL4EGl66wCVFPVwKn51DLaHNxpUYX+YK/zL7HxnHKqcjKK7HLNv1Jrk+\nNqKtgKJDm94Bqm0Sc228o/efu94BYE+640sbJBz7Wa6vQON+nRorD4GQoRnAxfn7\nOr3n1OGm5JM5rx1c+Hsfyag+TFxH7Ri0nx8PtJa2T78hDWA+B++6\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA6Desz/DllOiizoPahOuWF0Rmt6Sa6KEYI8E3SXhRNP/HywWR\nVb9ucXKJ+z5NvndD8iLaJ3rXCg2r/bDtEx5ScMH2zk03jRqfORfZe8yYQVbOCZZp\nkHZbzJAzNnR/F4FvHiT64E+Pery2jwcWM0Og44KGotF6y8D2ImlYz1XHxlM8SFri\nogFIDm5RRuQLvCZ+TYR78Uua8zMraZJWTWudu0kzuX/JmFEJwuozWAOPyUrDg0s+\n5IesNIM4zURdxmq07319AGi1PlmdzMU6RyUQuyVh4S2FpDYM6vDAE6NuWSW6ysqG\nLfNPUkgc7X/yIPWVGhg/kFlr8zmoNcQqn18uzQIDAQABAoIBAQCjcSRJy0TblQ2a\nCwXPjj06rSMa2FlLqK7j44d1R6Doi6rkw0tyreUfJj3lvwlbKHiDQXKIXRZjdhH6\n4f9K+ImP4YVD2OdjgjxVI+soDm2Pd3hXGS29xwB/r3LMPhyBv9Ekx2RoYrca7R4U\n9yjx9gSUfPckPAqQxIS43arra3XLqMyaIgqBQdNqpasOf9UOa8Ni4Eln9joSrUsJ\ne0t4+9yp2KPX5RJ1iMCsMTbG/qd17DdfESRG4G89OJWLwM4FYtSvVx704AseXUxK\nrRsYGxa+l/e753eKbefwog0Tv+QCrplHsCeiLXW/qj+G6eJNNIhLZF8y/ra+F4tO\nYMT7HxYlAoGBAPv7vqeTsfSFRtVptfn719jOH561pBtZvQTA6rcFzX35nA2ZKTRA\n4w2l8dTIZCkq3QeDFwupKr8TI7//D9GXIxf7ujpIqp5E/dAKdujsRiVg6P2rtJod\nc7JMrU0scoDkkYw0PIMF1lNX/U5nnFcO6DhujMkVWf8JHH8uIatCLksfAoGBAOvr\nRcxzTKoHOxDFtFtocqTn4jlBI75lpUscnMiTMbyPMV2k/gWQBU6eQtw4eGNbnYGZ\nAZ3RSK38TmEnz9n/qwLls0wnD/TOgACHkyHYwKJoC68J8BcnmpCSQOiZzyJakv6Y\nuOqyS0J9CWItU77n23XKEjBT55+XeAVcb82DQHSTAoGBAKZSoqOF735QwlJ3djBN\nNUnbtC9UAcAz2XnbxJGN+Pdf3Mt0/yeIdNY7ZIZoxwQNUNt2ga2muZppjb1bJBvZ\nwMZB18hE0qmpLEc3wQk5e4uMjf1yasEXE5jq17EK14GQXSnICPEK108n0wD/jshA\nBLPK9I5902ttniusTXj3NAtNAoGANHeDc1+lCh9HjIhbfwSwkmobjp4lA9/5LcY0\nPAs4QXYbBXd/It+PvpdFyVIpu/cRBVRw3pc/sanuCH9hI1tPvfo1sNbzHn+aSgPc\nvedFmBjJmGj+YzfkkQhltRUX0s0P6d0vKsryH9xM7O+ls0w/K2gAOY0/cuetwot3\nNd5dxE8CgYBU1F2tlnTq6gEG0SB/PydwDySw+S2c3mzJUUoO/sd2n2CH9nuyD/NW\nv3kQ/CkIFk1QCyDAjWdAf5TSVzC2RKBuhAXoSH+46pSfoR4UFwX18B+Xyrmfn7xb\ngkgplJEYuU2P0WDUYYJ7RA9cqpkgpbe5AUrjPdNQzXC/ASA2+LWytA==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAvy0twZWmCHh9hmvN91zg7bA4JgjqsElNBiRFmlUzlp1bWBio\nRIkJ5OIUKu2SwfYrJy/au4WKfre7ZsZFkhWXO8IUGesNWWwEKRlhNPm5x9MuQ04x\njM0QNv37cmX2qAgts8Ip7HMfshyvdqDJz7uWVx/aTpMSdabPoh+4FBSOqEbmvnJg\nZY9v+TwulhlsmVqNFVfCkWI672NBs9NcxNR9PTpVwepzwKRRzxx/+6qQZV0djHtg\nyfw8U3XEdMC8h3eDWkTDo6XV+583AexyvFpeuMyjjY02SQ3AIg6Wzhj24MNc/Zcv\nsJZe5DJpVqrKUpSP+ESl7jecwmI2k5P4kqxSWwIDAQABAoIBAQCjjIDydZW+9WVd\nk+h2T+zVKsY+M66i9XKBaJJq/6zMkv3MFfwN9baTKp3aQ7gjWvd75MKZXhsnYQNf\nXwuxd+sTYmtT6PRYpcHBb0brzSAURSP0zHYzYFMY2Js+OOCUy7pdaCP5dluNQmER\neWhs4KwzCLr4MXW/VN+YcM5Hz3ZjmhmbKDdvHYl7U9wnasid6CvpAy8nnLvvzRIG\n5bVRTl3ezwyPJwEQ3y+zDFy8no2t5SN5THAQcFlExFY2vVtc1MWNntC/ap9WTVlE\nr5KJwqB5WBNmi2cYI4juXbDxeOcm67hB41/ECppbJQSKhRysTp6gJENa/G5lWlCJ\nZ3zhQMvxAoGBAOTuY66R0jKLwpiqPHLa/B1ZO6y7ueAnGnqS2ylhMsjD+FigT1iQ\npnQQc10F8EaZPHx8iMz2hYff0JfbVV17mwnGmEm9ajj9WQSaq6G6h6i0IUNvLajO\nz2S6XnK7B/Ij/BX3JO65DmkIFu9GGOPuA8xKsZaAw/TPL17nLhDrXRlFAoGBANXH\n+e9Hr1s/xZXVVHy0LtmWsMX2L8kffb6p2glUzXXp19lu2N2VOpnkaZivtL5k1vj5\nl7T2X7F/OfyBOKkIoLsiZcYYsx8cvFmSGzRzFUhskmeory1Rk1OImz2e6oPGWEor\nHQQyDEFWc+6E8Ri7SX5FSCk/Ve8Us3Wa60vNRecfAoGAHZgTmsWO/qnChim7lr1o\nSgL3C+VYqflkGuRRIJBIGFv9BrfyiehpHCfkQeo6nWXBw/X1WUmlCWONe5QG/Qa1\njBX4KdAqoyNdFpNIDKgoUU633hfJN0FE3iiZfQtgg5TTSaOhSprxmfJJxLSzEwP5\nybb+Lg6HRmZiIUqRUe6bImECgYAaRacrhs3AEwg/L6ZgZOxVBTXYPjcFKn+TjOAk\nwW02jnReXkb8hDhdbTqk7wLxWwcoNdQ1AwojkBUaOO8zpsHJ/aIwRBzPOALVpyT8\nvx/gdKdRYeOVWC8Y1vLtEbiUVS3If5/jQhtLdEpvB5txhKK1h+IkX4o6BXtAerdr\n62Ap1QKBgQDibmiekWkYfwkBkXIf7qrm7jHvPCZ4hcDsLDHeQ9i90HV/bd7Ktk7z\n40JSQe3Buz18KB7xeYlWfX1pLYi9tM3etliAFxDcGTE+91oLsRAWiMw8T/ZVP4sZ\ndlX7gRoeYRs33uwaivikZwFNVP204KEURzdKRhhEGVnCrUVf9lX+gw==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0fyGPxvXAnpweHwSUYB52QiyiT8TQ1+PK35aGBulKPPT+PYh\n/ZHlhujmFYLTaD+abjAYx8A5LxDGRkecDmtyIJ1tRQz8NkK5/D7Inf+iqiXR0G5r\n9cjP8/gXRV6JsZF+UtvbmCglqOIiJGub1AHDcRc0Qm6Son+rpWuROrq32xcK8CyU\nl8L5l4Nf7IddmcLlVDRtF8bJpZiW8NEjjwHGf6FDE/4lr6HQp+dOd6L7RA1qt8Or\nNz/DsUm0VwhwLkLOo+SQEqIJustRDjUbHY1Sl4L4luV1Afbhh3UqAyuC8grkWl7L\nSp4prvszaHah9Xpbz9xXTDtirGqrff4XPEw5zwIDAQABAoIBAQCHbwSy3WTej1PN\nEdDKyj9McOSdypXFBXQUzEiVdfcfI6wazbz8i8XrLnl/dEKf4S3cVZ8V7m+zYHFR\nUMxhLIGOcZrRR1/AK2dDPscryH4EmaHqed2dx6gAJYn0ztWmNj2weWb3iyVG+Jqm\nvNTjd0OzxBCLrD9dGVT+uxMPOvBsGBQM2as//kGJ9LFAnvOvafIvHXE5IOut0fP0\nnaJc0gvh2DL0iuOs+z4pSUbHg5oxgR+09jJDfdRn0xH42TBnyQkpQJExKSQM+/xV\n1MUHEXgqL0IG5/lTMspTtr5zjbCWBk0JScVX0QnQ5djYvgGWEkSezE7s3+1rPR5C\nGBpA4dEZAoGBAPS27LUAu+4NOjP7VNsY1IRvsSvLMSjt1rooWx/tTKwZJbTr/G62\n7AWV/qTUU/Fqvphkj0WuYN2WTZgL13af9QkHz8eYERH8C6iVoG5CioMfv7SUBCqY\nrD9xRzj8o3rp63FrQgsX1PioH5RC2YJntdKHVzR0NduVjMfQT0ZMsS3NAoGBANur\nml5ExyPo+I8e5FfpmEncDdrWuaJyCaUSiKmAvuqXZN56e/47g2P/hNDOZedA99f5\npHtgR6I1axcRR1DGYkLeDkOBSwCcahjCOHFC092QMq3GJV6rSCo9FGBRJupWtLjM\nMrOafPefyQYb6YeHyOq8uDGSbxPjfJVeBrq9OkoLAoGAH/SbKG+2GrnjddJGHG47\noierrRpVixCzz49hhuPH9Vk7UUrXpeWBIKGbpQ6M/6N+zAuloeXJhGYSL96r/jOZ\nHdrFST0UTZLqOtN/JabOy5yjvgLuIFnbdFVohYqIFo7hTehBsY0lJGtf5E5udNby\nKVG/E3xxZAE9ZwJOt9Ff+akCgYBEIhKMIJr/mVfW1EcQuWRBrF4jvSvwpZw2Nexr\nXoJYssJXevgQX5ceIfeo6AuVUYJN26FXnwI43oarrfFHrXB2uvcDmwuPUfRaX4sw\nHgEmnH4LhgS7Ozbz2uQCB8mhL2l4U+V57XXLPCUWnvuszMHVhNlAjuGi8pEGpxZB\nP0XgBwKBgQC4AjvMZD2TlCpQpQgeiDW6XblGz6hoADGbe067z3kZlL+KeDo3H+xM\nVBpLSujKtchT1d8VuS77VAUvEpJ/Qekco4Dbiuc8cwYDUwwBkUWxSRrMLnYs9md8\nXno16/XJXzB8LKOaJIHXm+4MYhJCfgBI6PS2ZbectzuXSvd8CDLLrg==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAstI3WJ9BFcG52GleMFhrFhgNla6TQscmFNUXjmNjN/T40E1v\n1uLPV/c7bOA17dWhUTdWbhy2hNNiy5icx6cpDbm1NUoNehHVtYLGQEMnjgNvfY6P\ncNW5tkVcZpnMoNK7DR+vcWXdYvHIyW5rHrEc9DrF+wrXjJqyGtRFGwDotIb7tJyq\n8fM4yBTAD7Vn+IFTs2IjadNS/iCJK7BZeYDg3CaatGmZ/dfg4cPlvNxCWajUsYUu\nIXnfcIGaZUddCq10A6lu9/1wXraTvGnYxJr/B03zPHjpflvCkpm0dIKAuGNFcGoT\nUEi8pwtWlVtT6amnaY2mYb4EbdvpSWEt+h6jRwIDAQABAoIBAFV/y3S1GGJ1sQ03\nPBCzhegXcg9aXBC2tY+KX2v2O09a8sht7gZ5hZfSnvgsr7xaw3nYkputYNtbUsZz\n3qpbU3a7j1e+Y3k1I9BYib/aHAR7wgfvbMgPeG8c9NQdv0g52bOprbHzeYnAfzdI\nczFGj8h6fYTNp0gUAwpu6efCZIFDqFyUvo3R+VzrPI+PkZ4jdKvjIZByY0vVzn6U\nAfyOPs5OWOCgZOrT8y2h/rZpr34NukTNx0n2h7GfLs0ob97qWFxg+BA93ahGzOhw\nqLkkCb0QAod12FL1NB2a9y4RKrH5IWEuhIwGgvDOwUygz8fiVhvcwMu7J/rEQN8n\nYrVLxwECgYEA2fqQs+pdMOxU5uVCQTRkNOAdkn7XjgwEGazMSL9FntlV7aBBSNIe\nt6XZ4gK4o2Z0mJY4l3ldOUPgMU731V+/GQXEMKzoKwlqineQZJi1chfk6m37BX06\nb31aoa8dC78mDj6YWcIY0fZM4zN2xaJGmmKYOukQYJ/4kVWkqmSV7McCgYEA0gMk\nEJBRvwaUItWY8YBivJS0jcBg44X9y9G2ROUjI0Fk9tgIq8vcNZkrtHXo+qAO9BXw\nfWXZK2flJIYJ4eI47UpOAkrvxbpvZLxeX+/CygL6nXGXFG+8iqOYjwRM/6FCnOn5\nnKV0SCAkMCZwvu6vyifqq9IfFGBSPjr1Wi6qFYECgYADGLcjhynJvyG5ofod+QOP\naLui0CB5yRvpzXWddvIjPo0k8gjbYvjCvR1qQ4Wh9Jula0TkifnUDW3K5YdJxbFu\nRpGx61LlAZ4811P6ydySAVrkJanSOyQwX0SBVX6BIzcELsl1RPebS/dtptaCGjsM\niGgHNjZgWQVr8x6CuSkUEwKBgCxUsWY1sKvMLbT0taY03aLFGR/a/hjJDfvaNslw\nPOySP0fD7oClNcSyooEjapyM8NshTnSJ7T+2XK5LBQcDg9TDHJhrTRXF9wGiqaFC\nsTGHU3OsqKGNvJTfV7LIy6AiYDdTRHeGjXc0Ia9wTdhf9geMSYMfhaP9eR7dvzDc\nLNcBAoGAKm5TzDtEkBGgBGK+1v6CyK5Jl5mMmz2/+ckJrH5t8Z46VgaERYOMg1Gj\nEliKUmhQm2BMRKUJGQZZUsFJlLCMtLMFjfIXpqEDBhWp1yxnuxKQtPUHBLVCdPcR\n/3DXdOsUOXjNAGXokO1Vog2wMl6jSotrr+ELXmApNCVq+IBMFCQ=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzgRinMFVm7jLWXkq02TiBb4YqSk6YkaHsDdhObC5Le9cpknn\nQkknNH/lAmuxNdv5g92aR9EOg1vlRKSqb8Dle9ihpgkN4ljKHTTK2GB0mNvx1eiJ\n/OKLbvGG7GF4ce6ARXhK4bAkEIfjTC7dlmCMBMVN95CoN0jqkDS8vgP/6+JeZz3F\nioqZ76Lviwa7fGZvdgRW9dewsfb868QKr94mT3xO1Qet7kxW6/05GMmGWxHMWl1K\nmUeu509c8+yfRAx17+uuMyqZ4rQ4/rSrUt3BamZFoHA5ecyLOVBFDojk9BGHdDOB\n0BWDfKsPDz5rj9LJnyV6XsaiHeY0pJ8xo0zsRwIDAQABAoIBAQC0F61ErtVxXWeV\n/Y/sX8IdlwHjzoCVLGatKpw5XD1k5FuvmecpUN2OzUaaiR3OoU9LcMrPgPcY6ZpQ\nGUpMiumRC9yzUjLq7Qcx5NZ1tEv43DxZ6//EKx4Mi+KQfNxXbFCBahi7lcjREpkm\nHcnjDN8ZxIjVo4UJXxp9mTxtNv3fat7FOazGKhWrSvuk2l7d7mFpsoiFFONMoW1q\nWx0zo3p1WScq136Klw4+qMJ8abLXUoR6jVbyEJ31MFDrereHveXEU5EzHag87Upf\nHY86UCIplREPsDKfeHLOmpkGhkabJFLN13o1yoC9/JGcGZ9GNyeQrjbdFBI4qdhp\nx+KP2C1RAoGBAPe3azpkz5l49qtuJoQRn+/Kg6Vm3YMmkdn0Ioc+ksKrUiqOcTR+\nSok/SZgw0MotbOnuuRuyxx8/4kicvZRP1lCJnQdPqhx6mFDa3LfymQiOG2c3sFly\n5miX/otEkDLBU7hMl4pqPRdrtTTKTfSkohiI2Z6pEu8cCW3zDqFA8aR1AoGBANTn\n/5O3+v3LT+zIApkrI3JBUpYx9re0G9XQtT1GYEP94DZviwUYb3BLFmCLr/3HuHmo\nBTtxEC2tvkoUX1oEdiVjw17/Grx47jcR1fldSU3+SIP2p4CnJK8bH8UG05f2OZYy\n1TUjR49kWHTJbLWV6VuR3ni3yf3AwZN6N3OnKwZLAoGBAPIVp04O3S8gHviUEkH5\nM3NVV2haadpU5C67Ps8rIKPsZ8U9JXbmgRM17Uc1VaZv1EOdz/s4sQ5iEVNjEoaR\nq2FTy0ks2pMwYBCMgy2lgVbyAefSbZ5NAs1u1QzneYCQnK+88lAL2R49XX50wtB1\n4A/YFczPcLiKjtCLu11tqXlNAoGBALQqmzHDGCfa/AiwfNpnEfjm5F9rao+sX33B\nvw1aV65R0YHfRHKMaglJ0Wbj7otAjpCMqdjSZdFx3LzNnp8LdXtQgA0MrfBeGaAK\nfNsnoRfaHj1l5ftN9hIkTu8pRreqyrKa06fC8hSa4uv5ZAAKG069EtlvFgShMG5/\nlxtMlHEfAoGAYSLKZ1sYBmr7zwnCl+TZPRqCWEjUEHcvNYzPxHGwKKQsAXgnfFwe\ngbxnhUWFjr7Z0oPg4MGDs//l//tu8SxQ2ifaXXEjjxCal9mXjwOvcxpUL9+UxX0Q\naoD1yHLNLZL+apdiP9kdT7AxM++EggokHdUyra79jgk4u5k4NrvymSk=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAzZ7B5oNUhXHJ/5eixDCb2jKtY0Bc0IuKyOB++zkszeFMq+xe\n9QAkKKBl91to/cMvLl/SzkVDQoFLfzhA8tBek1G363SZBWRQnDrSFRgyBYf5zvaZ\nIWcPj6+aq+8Jjm8bjoKdJQAZHKF4UaPCVtLPhmyNoLEEgoeksWOw8lqPnGeP0qUR\njDBrOFGbfkTWuJtVc8DDtcrICAXoKKL8HD7NblwkzXX2e5ukAqflxH0A3lVkCW0L\nPvt6TMUne0JWdXLz3bTs0KdsGxmfJouXhg+Rs2s/ShXEikKqEeJbdwmUVj2su6VP\nyjaaozeClrbbxgekg9aqShHpxAkav1/aXFUcmwIDAQABAoIBAQCA5YORuEQ0vwcs\nxWbQa9RXbStKK5/DT6Z5lFmlrMrMaO7CW5OPLD5+hUZGULQ5V7Ds9j7U9ZBbUWf9\nYUs9q6eN3E1aOaZvPdpWhX0KojdBRmMv/WAzE0MrBJ8GJvuroTT6GiDef/g9/W7K\nZsi92rsi/kuuLymvlc6FMQRMeL7MAPJwy3FcKC++vt8dxLFHTZ1pobVkwcRwdMUg\nJCP8vcTnRCE77OQzsu3yowcZCMgnIKOTCxp6SOEKDfSkd3M45d04ZKLAxVVHKbUC\nBtL0byenJuosMKd9X2TcJv46/ROq4PCjUzKZkhn3dPy4LyttHu2j4TbSQey9X2gB\nUbVpWheZAoGBAP6KdvsMekY8YgruJp6EKh/NnS02aP39uQz0Klp8lnGTV9MGq9lm\n57CsmI4fLH8Dm0dtR8SHIwjMOxZEpB7WVehTFL45QWe7nGRChyq2HSsAtrp3O+6U\nsoNzo2hfjm6aaEHtRltTKIXxmjP1PoSdjuxxK/CFZMNbgihP+eUmj5VHAoGBAM7M\ngI1B2zijVeO4xBPTnWqFGZe9rnBm2iwJrE5+J04IyRTqSdKzVpbfb1/YgvEu41iW\ni7JZSJEwSV6ibMrqIAqmGbDyiWqucifuOA0neIqjYpZ9Gm5p66/gp1VvHBdbwYFJ\ndv1PBu4hkOsSiACKl+urTzb+2m7NMuwGXY9MBTgNAoGAN0Wr9nHML2o3Q/ZYzn0W\nhJdqdQHmpJiu0UBH3iRFqt366Sj+shOlZfjkm5/rUp1e35C8T9GkaummPvyiwst6\nFhvMWk4mhLb5i8/ieATZqpfaFf2ENxd0+BRpPGXbkOrYjtBOQdB58TP4byXm8Hci\nLPeaOf1NVxqM1eIf+oF4FksCgYAOIU8PV9ag0hhGTROovybcZmfPRHis2XpC1A5f\n9qK85EDJGxEcQoDCIlY72FFqJWgHX5IAB7W7pe79dl+0pba2w46x1oCpN245aD+Q\nNog4AN31pmqt7LLb+5+zaLokpnJcYSauPD4e+1Apn8SHNPEYe0YYXeWS+JZoJi8y\nYWFh6QKBgCJYUmdm5uASdjlPWTnmYg8/MmQ/ruNOyqGsQ3lDtdqYG5K+qtV0lNDP\nRfrw9vLbSMD0x3v6PZFwr/Tj9zwaGrWiJSFWrqA5cNVACdmFeLa5W8shEnk0Qq8l\n+YYLx2HtB3geAoNbKpfOk8jDZQS6neL+fmZZ2esBfDflgw+EOBI2\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAvwAw0iABJLFDjAKnHztlgV7xwcs60R1zmwUF3Cqz3E2bs9v4\nDJRbZ5DgnB6TACSpGX3hs//Qpoihu2dgf5UguetNruvoYmY8z2438ft52eN0NxNd\njNX8bQR28XEiGvO9BA+rwYDixfjxB4Wtw4Cqkj3ADNh66hr6Br+tq1jP8UdlYsLw\n6b2vtGGyK3+eSaF1GB9v/Y8BmUTH6GJ6nNgEKKH5ZAaG5icLg+89+GYsq7R654Pu\n+/WFQzKW4omHExImwE72g7v14PIYQKVFxaDCbrMHpocJu7zFRtKW0n2tcQ6I+1jT\nkYqe4qGc1lQkyIr7eSkB6RfaH2tseNTpkgajjwIDAQABAoIBAFsxwR2ikEkvpduk\nrJy55FYnufBYk4WitT7P4CNl9Ch8mZn/reh7sUS6pLOF2ZQY7PAx7WDfBIVu+SGc\nUzO3RT/fvGCHXJYW0HQN4QJVmrFQ630cbu8+gF6g4hWXebw6ogXdYZlra1sw6Aw6\nZGPS/B4FA7xeZBmAtd4hCM+mAOrm4pbjubaS/gHe0uvJhuM17hnzSrA5RrXcsovu\nfUm4qf4pkkZtJbG9dcAW+AZAwjmCNQBQNHY614iz9CPt3nR262IfrAps38Ra0VYj\n+zwDLjE2WeK6z4RjSVfFhJLV+JiVVlN3TSt9aYhGxXe4NW8segsui2tV3HsWEE5b\nUSpxaVECgYEA9SCRCAgK5qCRuDhcu3HVZbEXyiZ5pTB5+0l893jTUb6w+RqtR0le\n9GMYVQ0FK3VBAVy9PuJgEiDMgEg1OoanInaxyB9vaUbdHwN4bDY8NMoDg+y7Vpup\nVYGQRG9esGidcnzVHib4Mz9AW1gOaC/Up4qDj5HES2cX/sa12vqrm+kCgYEAx3kD\n2jfmC2VXETAO+b6VfRLEjmNxCFh//IiBDX4LnuDeLCV0NoQKSgT59RuTmAHyx39D\nbfNqaxAs1DJL1iFQY1IfPuJp960p/mjtDrSnsf1M9lQKIxnFD/FhvC0ex8S1p+KV\nJVqlfaXQwJGfridJsfqJemipAyf+55XEpDyasLcCgYBvG+XhaBPY2etzP0j5Re3e\nfFk2Lh5xe/mup27XlZLjuLXrE+Z7K4y45bn/wzkWq71darX1LRMy5F8Nvq0x7BsS\nCg8nkOglJhPQnPgWxLahjvfs8n8wELq+oU3NV1XqTGEhpefMFQnn4MHBJbbSDuwr\nTSO5De+V5hMATv3bVkg3EQKBgAcAHD6AT7Z+q/uScDQbmCt3iSZmHwVn9bXrJHxQ\nvB+rTKDH/7gaEjJe0lbjzN4800RlFvy42jc/rhMUYMz3Blc1/pq6X3WtwheHnyow\n9OGuPhE3CdQJh3zzv7ZW7wmbwbROo2VYNQ6fzx+y/KOkBVYVsNV27teJg8yk2O3a\noqMpAoGATwSBAVVOUH7dNAC31lHMm0tSknAJDAy4VJXiLnPWgj6eypzaRQ4H1UCl\nCIyGBjoWLWIo1eUHbBjO7PPJ2S2sfC45DcTOHRnJMeZLgqZJ69SPxZMGCEBVCZFl\nA16lpfpfeM+NfzfecWSCbrnOqe7t/BIpTRIT4mgyyYKxzZ5FpQE=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpgIBAAKCAQEAv+BrB6quw79x79+9kPl71LrO+hM+W4c8m/cdw2LzSSENAx9K\neNu/nwR7GENayEZM0mLF9zaXjYMS5DwAxfRyxdsBVTkjGNHhWfwwjx4SZPDAWrPm\nYlIfBUwtxcSwN4mk2dqK40dxrcI0kgBw6bUoYhf9tr4nHgNMupLO0nm2fP3b0UO6\n/ZKBrRJS6ylm5gcBXUYt4o29pm2orty46LYyEiOV3lu45NwoTBc2tmKoSX4GcgPB\n4I6SatiLN1KICmYoIcPmd4JU6wz721j7u1GHEpP7+axmVguJ+75QaSgs2BJikiwQ\nrS9zeR7dZbVs7LGJKO2+v9hC2hMY6JsKjqyIMwIDAQABAoIBAQC459e8x6srUG7I\nLuFLuGCxHGUhcTICmky6MGYqSE0TCBq2C42E8p+y6mqFc0MlBTo69Jy6I43g/ZNT\nBs/n9C42pVqfejRsUrXMNc/F28D2LVvxDLTTwzfJryPdFjKLEsYeexCk/dB7Fk/6\n8xnc3otQHLaPSKgBsERc9+T/x/Modv70IUEu12xsd3r/Q3qgQm8PUgFzt/EUnjon\nRFirztBsSj2sjkKGoRIytt79kQDnRp9ey86svdZD06O6GaB+HxHvj30tbfkYWf/j\nAKlmnsr9tDPj3YxoszJ3SL3yQ/gk+xtZOTtSY6Zzra/Xz0Xz6ESx+0BWu1JxiN16\now91PQ3BAoGBAO5wPCqwtwgFazLl8P6ll8G+8ZjCD1UpIq4GgRVxIH0UBE2FZFbI\nxQPuMzur0D9HL4bPY5mhrcWgsDbjiIKSUsdDJ2263LBJuDiTAC063xKaXIc0EaS1\nDW7IboxyMDgDHeRtPEbrlDl3U7fFWiaiGP5X/O4X5zFCRyieBYAq1O25AoGBAM4C\nQkjpDw3qHRlYa2yptOO+bfAUWA8qTBIIK+fCVSSD06dEom0yRcUHxk1sBFnU7RNB\nnyVfoMJn9XwTl9hlSSCBmlxgDakk+Yr78fXz7/Wox8CvSdkph+DirvfSDuOgCULl\nzDZ4smzXDahzG3t1tzM8eJBgW+m8ZN8TBGb2K3tLAoGBAJ2J8GbwbW5STjrJ7IoD\nlRpA89XPWlwVGsHKsF0faqzZneIuYVZpvqpTJuylH6m/eepjelZWmb765ZLOkTJc\nRW88sn9wuEGN47cVgUdhH2RmMv5t675clax9p3UKOUu423ZCqlHdcwjpC5pfPapK\n7aKXNhmGF43XUbbHebDuG9OZAoGBAM12PUvgfa5AB6OmpwVqQMo/vAANGkKNye7W\nDYwJKsfPXsHd1y2XTTSUa3f+Olyp24UwFpy8wmYCLzj/hZtCcWulNyHYfudqxCOU\n4h6Om43kNs17Rej73dQx4ZWzADyP+YyqCkFtoW6iEbImk4tPvVaxggFkyWbWCbje\ni65w6K7/AoGBAIwwcGJCQ1dswX1WXkZ7nAJ6+PwM0md5ZaGwx/p5si1WutPUfn7e\nW4v7y7alfjsG9BZTATcH+aWTNoWK4MqcTIDfITIZDiR94f6ddJD9l4fKHSpKf0ls\n6HOzhneH8cdGjsHjBvmv4oMdKYyWRyhitzIdpwGXZcFKINwqaZhibVe2\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpgIBAAKCAQEAzNGyXquL8XJ0HN1xdCfw0lYqE75cy+OOQA26clutJIwqONvh\nIL3deqyyyqsgmQl8KC84GJYrt1if+WW1mXViOFdByLAggkk/A5E3uw2kqdrulABc\nrxR7R5vBPkoONZrEsYKwLBPuTnQw5OAwjRc8SjJilB6kXhQhKzsX7lWq09L6CA0i\ndbDKivTPdOIQzxxgbbXtNM6ohYTAa30teBJ/vBWEVjl85qr0VMk2RE9ffddG8BhH\nrEqEFhIRBiiFsgk7sOP0kQi4D7A6oiKMVS/tbVE80tAmcYjBr6cUF3b1sjIk2OZ7\nq2rehUaC8jMmsFa/2OVEUSDNqw+M13L0Bv1/UwIDAQABAoIBAQDBsXlmcAKa5isK\ndRxqxa7HqzaFj1vAcUVAK/trVGlMC8WOGdowFX3TGmjGosjkEQU6F1WMYs+axxQa\nTWZMZqbpMxOCD5XewBf4bqdunLtg6ir/CcycwdjH4nMAThZb+S4T1Mx4Eq0lwlnO\nyHkBpLyeSkHRY5z7Xzvy3yKFgLxZLhWhlj4rrG8tZxXeFRIFaT8eK9p4cUoR7NO7\nOkvfra7hN7grvxGunLwaDeF0M3prqCX0zLOw/Ol/4jNCbCijPcItcJkoI4laDzD0\nBVYFL8Xv5bMxzKXynaT1ms9zI2QMwyeDcG2OnXvu3KmQwYgTcQv9Jox+7h2UafGh\n5PzbYKaxAoGBAOiQuw2Jjfl4ygunxba/G/m5bdBfhrpgQbPMyXgbagQ1i/0Fg7nR\n5i0dmCcQvWxXJO3MHSmGZ49lwk6UX320GAeOW0AzmDWseYBl7B6lMDrsLjg3GyJJ\neugT9HlHecfAnDSKjeNvfPeP9CIVaaiAxCg41U4W3sEact+/deH1VmFrAoGBAOF1\nOQsSRFH/uzOJUwYGMLwgb78zRNJrmgpYAM0Gq3y9hmNDBCVflGkTpv9Og5oO9tfD\nYoyYsGACiBy8bU7CX6plcileUx9ZxfAwEUtlw+HPHGeTEhuZt26fFOD/zQl2QBVy\nFG+tCEHN82+B+NrLDxgPaXGWyuJisLvl+A+tBIu5AoGBAKc5iobNZf0AUafX1170\nRBVotAGk7qeNFzFGC4gFjlHAfwxMrs8qkqvWH9XXP51re+5RWpbFQinmLbV7Er68\nhJrKTer4LnZJsoQUxZezh96WfRWG6kfZNjsyPhQGxRZQ9QECr8veGqmYJR9s7jBv\nhpy1YQtpfnqzne4tKYJ0esxJAoGBAJeMqWR0yfHomdhhpao1/QpoL1VxzAA5jhkH\n4LnwktNNvoj4ok/Q+LzNFgMlrrae+nQ8hQEHHf/bk8zlKAC6DX5HpL6EBhHb0X7L\ngMSSymH+SxSgOprM8A1u56T6rcN/dkI0JeKPiC5blxhMYJAJ0MKWkYVmHEiimDQl\nuGIpJhYZAoGBAK83MogHLOPBqZhN54dNzXdWWfcp1q5XDsKebB1VrALdehx2E10T\n/EXgRPUhDcnPj3p/Aglg8joGMB0HSsrEE7mtKjF0Oya/k/JElUVblrFXnBXYf06o\nAw23lRS0VKQybIwfcvRUsfXmgCLaQqQhp4xFX4CrQxbhCoU0aoOnI2hO\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzclWfjoH1LZuG25xig/Vcu9Gaw6BSGOLzpMCnUfRBNvtfKxj\nuf8aZ8anoAD51uaIgwHHBzA42+TynfVmhMHZjjbH0IU8l5JIkzb6MdIQCwwSlj/K\n8oc9i+3o9V02Q6E+O6vpTIm0nLJRAfmltF7bmhAfGZVLPVB3KQDEn8BCLiDur1AR\nrLaSTLihKyvfZgHjBM7VDDIvxRZmFQXnQj7phIdLeNrHV3m/MDgM1WsZrpfGuhlK\noaD57TV/49zTBum9Sdtu//m3KCIHiDk1Z4Yxe928+/uqnbVdRpUzNscXTi3F9tXj\nDTU6OsUvhgxlWP4Ds7uFZiVAEeCvlSTEoXDS9QIDAQABAoIBAQC7XGXglUtN8y86\n0IqVbicj9xDBJjq1QtUmppjJxHpdFzQZwAcocEiFVcyGlHzkmY6O4M5qEBCkvMXc\n5XKKysgegGfaxYROaESlrHeNZrlupXZC0CAiQtnZjin9Q/nFB4jW4YWPRw1nYb75\nsmeZWohMtALs0w3m7F0huu5KmMxom7T6j7z0Ksaqctwl8asQUl2s1oO32JEhZ8uJ\nXZSE7dDi4cUVi+VoQDr4moh52afaPXjqWXjWdqQ01AhezSyEKrq6HELrtbMeDP5t\nLTIsY2dsBELNXHlSZPNcf4pmdv3pSqgZ088CPbh4QwbSv4I168vcPyLs+5B7VQ70\npfwcj2KBAoGBAP98sZu4I0osgx+BEehodSH5PIMqM5X1Eefhi4co4f8YkXzGKOLG\nulmpSR2oKkpZsCI1eXh8H1fZ1uPjGHJotzNC3nm0nLLIbNycUVTnzTt2lCAPi5mC\nTTVp8bO8IE2tV/r/hI7SgYvEQlzfSMVNWL9lp/TUA5EJYD4/i87JS7DVAoGBAM4z\nGcjDdwshEJgcwdL6NuIO9uMgUaqGBtk+32aAmqihd2fck8VFZeY1M0ZSBmpYoKH1\nHphebJg/sA1NiTiNqDk4MsR2QExe/IbZi3UUTHJGiUyrki0lHETxsjBCD3Cs74vI\n10PxZxVFteueK1BtittAbLIZaqbZAntFpFopsqmhAoGBAKh6wF0iNxNo+ydBZF/Q\n0r2OsJsGr3IKZL53fB2rli01NGwF+VxjFOyfuDm4dfsF3iMRBIOxxrGWVCF/1tVL\nvNQvGqtDsbosda2d3/yPyEWYUuI5niOjS+sXbG7MdrkCwOwiqHXO1+Mlj2XTURfb\ng9Tj4riPP7Lbbf5exYGeOS3pAoGAHr7n3VOn8HThsIy5Keg9Pr/UeFFdW/vYEZSa\nYwJSDUrmLwpozkVmyEiueJHKexjz+rI9+aI6twjoC1PAXjphFwcWKs0nETwok0Lk\n1HROYcu6tT/v7+NUr8MKOr/e5YIjxcgQsQTRxg467gMLmlZ7Ge4lRvkbOf2prQ/Z\nTchh54ECgYEAoyP99H/fGJtwU8bP5V2k9qU23g1W2rbKxVUoOr+19IS8L+BdAOOu\nfsIcbkEg4tzP6mpDp31pzXQsDAXd8QAgMxLWqW0fZG9PEWI21Ike18yxVFylEs6a\nrdRqb8qb7K0qjKCCDzA0+mxFban408kOMtQwB/8fKfZiJTLuXALKqIQ=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAl1TlP54KhreOyVhczzqlKL3tjztKD99UxoGoG8ASARDkD9JS\n3Kh9CZ4HqnfeYvkWSMnSf7qcURggsMQFiAkGbs82PWirAaD6+lLWsO/dOMyVna72\nzInl4D8Gm35YQnYvJ24O4xxfXW7FwW4aJbX1B22QLJATLTVYG5CX5sAfOQPMtFPD\n0xhH2e9aK7klxssjwuOUc1DVopG/+wohY+zMn6wOYbps/YlkNPojHj9hEmPWtGPA\n7z6qT2YY//zZFSAGcArAeDnHyYyYVu31ue3S+r11BO7lAq+d+k5mBRwSwK+X32ND\nGdXtJPVMPEBd4zDiFMMKutbs/XMqGQD91/AXJQIDAQABAoIBAQCPUkPbwF3PwMBm\nSF19ldkdgOMgJ9K4vZaYeeswfPOQNuMHaPY5LQletmAkPmpL6gQE47+MYLvywZsU\nTlG+yG1jPLPt7NcoL8ICmbni0Qj+5iM8t5vdkyw2+PnVAg+swileeol25kQbRPHd\ng90XogByDH3i9oBy7HaJhKOYUpQ/dbjwzh/NE2UJKfPKeO5rL/jfmo8nwfc7SWPp\nsB5686rHZ3VJ/iwZeYZwhzlM6/VLiU/D2zYSqHDb9oqDlvfEMR9Ydb2XMky01M79\nBOsNG4xmt+JztjHwgJCpAH99BoxydvMfO7C99E9/MBPpH2AqREeajvf9amBHFp4r\nRn8PdNUBAoGBAMYcepYCrhuspincgVw1cfWGCIX94TVnF/L1ZpDgmvcMYYuJ/2yI\n7a49vq5uzw8GqsVQ7+wfRq/r/fi+mrSB4R0Ixq0fPcN1v4AoIraATq5muVqtGDRA\ngoXlldNrumW52oYXCcXm9OGyzB0CvBxAgaJXurbZy8qNdu/Ekrc/6x5RAoGBAMON\nHKt/26/UM1/Wqe8AVle7ZeBFbBEGM3kZrxRWCkqqbbcNkNc2PS1rZ5BiiUXbF6uo\nI1RVoBhUoIGNUVWDBkagk1tO41pNXFTFxJCicH8o1/dA4x+IG/bdKtYFNEza/+AU\n31I7KCD2IWJ2bikDfkIzj2+gjzX3DymTlR52adKVAoGBAJ1IuO5mHFKuZJGxliZd\n7CbQje3LXURnn/Ttbcux9nYTJ1KAcgB6SbFfJgcYxF0anvPeyUx+nEFJLC6TLQII\nZ3AEqq+BsSiAUFRwCPc5sL33okriR+gcG0QwvR3IVoky39I7ci/jqCGUMUcLB8uq\nTwFTg1JuYcRjQb0kJJNGNOWxAoGADbXwI6okShCzxW10nrhEVYRjITVc3Vi09TGy\nz9c0g4WtiZ8e36gC5BNawInYHBi/cR6p03jpb6tHUa4J3NgB8aPCkS1XzXYNGjh/\njrCE+LVxZvmasxRE+asKHNVilFzqgdiUy9wv9ReswY2SLbf7+0JINUhpohv/aSfa\n1bbxtn0CgYEAnK5r0JzxWekJGl6qd8jWuwkNkJj/qF+KS9whwRTyWeAjxNSNVjG0\n0LNfmfzMRatzZ+2Dq/xUtsy/9pKI2STe39l4ES6gHrTGX5qyGz7YnfRi4ATKsgyj\nKj01RNgunosZFw0l6kE+bPN98BBIdh678XULIBUvW3Ej1u7lsNbsH0M=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAoswgvPfR4dwpfMMcZDNKQR2r0w1i8q1sh7RQyQNBAJ5d/pua\nmsl2K9AYMC9+0dEHWBnBodcPoWxqS3ZN4X6PMirGbHHBR33w76p2bMPdmyNwwjfF\nrZG2X4hrMcspqdk4B5+GOPgMMAEti0LQmKRZFVQ1K7Ub+P0wnRDAcZCBACnPHNjW\nFo3anWdP9aL+2Cu9ZCf4tzrZ7aDRmC8ozlwb5aOu0crvbOqDY5o1qv7uPoui7cTv\n76VptejWo/KV6aRdslZN/KuoGvQ4x0lxt9FDnb8Iz1cKsQLrgHeqX4BFPhK0ier1\nIKzNyKOTN25LqNSLdeUyjNO9OYc2bTEBx+4ZxQIDAQABAoIBAQCSOewGEErzJoAV\nUmOEFRXTW0Vk8xc3h1J+qhYOF+AhIB6pfTbnZaHBRM8VKPQuniSMm8XzCms9wVXQ\npljZ/IW+t/E2ow7KmnL5sxFgLKY2nSTBLrteZg17zdsLBMNNjpDnOACl1xeLXuN7\nvO4EzUo/AD4qHikcAr/RtKRG3Vchvv1OBxGW0RMj83cCN5cz63Df2O101nUaLL6Q\nxvcn3vVhJhGhh5dFKHxnsPCCh2DydP194He9Pey9191X5BB0T+eqPLvxO9t2OLku\nKvTO5QX5H8imN+J6M/S4vjpDH6Rw9E64PQFE2um22akQrvQI+77hW7OhsQBr7HmK\n+LxjF51ZAoGBAMx+loJDrOdyvvxTmXCgeh+9SRKRpvTQsLfquiYxXvaeax8ZiwO/\neuf6Y5E+bDfsez463WigYHjR0W7dpSfZt/fRNEmG688JM5usVi65qXm3nk7k0Auh\nWagjEwFu6lA6YNXketD+g9aMuGhnPKYGHCWuhiPfZBCWrgY67PDp7zzfAoGBAMvM\n/YV8/F+8Xig05U5Tg9hP9sTB29WGIzI5gEhh+W0e8/ETNr207zjQF8pkWHUHCauU\n3Nc5vnCDfe3Ou65EYvKvqs5BgwNXBS4wJV+recLK0WvhUqRIXlHPBliegXwLP/c9\ns5gXkL6WOtJCoTfe5F+z9tQICaHY0FhSQ3or1NnbAoGBAKr97rxd+mZN/IThIpya\nk4OWs1Njl0d+eUZQb/cfsVhmfwwyP5uSsSLoq/j3SWY5x5PxhNHHyOM+DvG1RDRe\nSQZfWGli+CrWduDk6euIM74jW6x8h7ox4NZG/c1lAWi0Z+RyeH/pUjRE1q32JTBu\nS3r2xBOa6AE2/a0X+Kg59GHhAoGAbiVQL8EpNSS9TsWn2PlSHKq7GAQeJ/zjMNXa\n+0PYZp2AAh78SvNrBy2QbhZlqHoxQ8akxL20q2KlwM0mqjzTrY47plXJ1RhG+HuU\n92vZ1ul+3etdmuRx9Y0KRQMFwGDkJV/3nI+/7wGKsPDJ6URR8Vd2Y4okipB/qfxr\n86+UzlkCgYAG51sL7j7X3aWvVylR+/o99kMnvsPXR1aphI1AZddrTzdlFAp4XUyn\ntIcW9Qi3DYQpHZOlfh6KjCD6hF3jMlh8h8zxx117tGJeGMRKSBlfzjtpzwwdpIbJ\nY4MPVaQQhz1Pd8+Qm8cgEFSwmxObRxDzFMJqQzjSBgSbssNl+cG4Bw==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAosjy3ha3nuQZhiQ6qNeOdjIW0LAxXnJWt1vNv2ZK1hRqrJEU\nHEtIY4SEMhPi+JbjMszxUlHSTugC3atmKq1M0+31I4jVxQbYRCh9bZgAt6J6HMjM\nOn5un58M1c+anJrME66CD/Ae54VLz5ESvRKApMrWqLsbccUdFITQxp1nR3VeHqLd\n0POFdO5IjCn9gDOb9goKaKfvlQ7S4YwV9UCshnldndF9zz1XuIcCbrV+CZnUOvwY\nfs6y0STrM4BcMB5AQ1BAN9RWI83kFiDZjhJSk6ZuSMDw4CmZ5onqABvfJZHUm4u0\nqr0CEVpXHeOQlnJph6Kc/MG4TkVJOlrYRhBSJQIDAQABAoIBAGvkPjx+2BMi4dvr\ntUllHVIvnrmQnhgYNmtvGG5McTt06sMXoTmjmAYFtcyvK5dSAqIP/b5u5rJPyl57\nlMqidDJfHEQCFJVzFPIP2BpETe1obgEYAWyfkdiQBWTNJY+4dZxx5FiF8SXITlfz\nsnTIh6Uxn5bT9LuyRrTr1qhdeS99hzoiCFBVk6rlqZgJjM4lmqc83C8YlN5vyoV6\nk7Zxs9Ku6H0Cv2ircEUKANEEpY+RJA82n4+zWuW9xisA39Llt5E5fWS1anLhWW/T\n1rmZqX0c3ACKHwQ2TfQVYQBitA5++WrkM81ZWgzh1zQubwf9+sMngwIr3jODhTEF\nky1uH6ECgYEA0nL+gVmifZkFDUJZ31bJacwGBn7NkLcRfT4IUk69jKL4n4aXfR12\nydCFrdxsu0a+QDpSa5kNRDqly1maBOMh2MWXYzQyWxd34IeOTsgIJ6cbj5ybJCuy\nK/K11ots1uIrl69klAQNO6x4o4FDGCK77/JMomAkanQu7w+JStBRfu0CgYEAxgTg\nGhccM1VRL4/Vedyqgg1+Wh0KTankaSgetipwFjEC2TapXxh16CDjd+dEYOENZ58q\nvZGMZd1i+6jxAmqps99WXbAWFuNFR7WapHwYrt18+Nfyt3Hd9Zjb3ehyia92z69+\nCUkhfU/oMKBoIeVseHT18qI7Vq8kcIxFP5+MARkCgYAsnnL1a9K2vGXEzOePIbBs\nfPReFi5xW08a9bb/9G/dzIPYFsoxnbicy1g8Z3qqLjKSHYtyAq48ZA7Y0XOFS77W\nXfZd8ygmBhDFixgR/fk0pwqB5VtDTPxmsvv4s/DskmFZtLcnZsCDgWrGh9Z43Ixq\nMD4tQyMVavK0ft/F++r7dQKBgFy1TaKJbVePOypUYfLi4CVxYdWYwQkpYFnnQbGx\n3rZVDetaTyPJlPIv669oZDgjRZ5dA6qrKRtL8hq2tteVzov7JTlJxp3Cm9395T/b\nJcdAxVDP8b/3HJ7uiQm02WMi9jI4DH8WcQp1TdTuVLKB8i3XPIrzb2qfJyVWVtl5\nvheJAoGBAM1OmesJCpGa0EUa/JJxlY+hqwXSenRThpoSjOlqBCn8V+5gAqKzYe1A\nkohbi+x4PLU0r7Rfma3vY3LWgtNv3lDelHWFsC2YaYcwN/gzBFmTdYBZNvehCgBc\nS9n8SqRjVTaWOpMZJcoluFGLDAnaOmJwas186YTxhx4ecBxmCVQS\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAm9V707MlNfrjk9bjSmmoxLnzYVQ10CSVj8XM4SZOFoqna05o\ntSWSN8YH0c5Xvum04f/2HHdyEDegxG535FCe4EOQpXGMkHYv/lXrMOGl10pxcE6A\nvrr7WnnCV9RWN/3eNimVixIqYSJGMRoFqtvPrQKHapD5JZsqnp6jbwbGIahrb2XB\n8Vqg94LFl3eBlwbKiQaxpB4tHhtPAzpt5kYujyCGoutC45dLPwY3ciiVVlBcpZPj\n1S+MeiuoAIFUTWhP6I6E4CvcV4NAo6ZCly+Oh5lBq+j5qiVwLVqaJpeT2zDeuAtJ\nxHLUUUPldNdj660+Nc0bfd8Ann2itB4D5dWpSQIDAQABAoIBACNLy2w722XKmdlW\nhszH+c31QEb/R+EJKEcUSj+E6ZL5fMo1TEobZG71j7fZwM4hKTM3QotYEqHRt7xG\nOSmhiRrKsuyGN4xQ+LXmyAqeUW3eEquZXFfXCi+H/JjgI1czTS1/ZbvGNYsitDtS\n5Y25k+Rf+kQM6iG2b4TV8baoFiBxJsR/1l4frr/6H7FDR0VxoqKZ1xl4WW3kKzTw\nqSjKQAkcQa7bKEhbZGynzhLHdfK4AOPMdpE/63Nvl3Kp+As2alys2OpoLoIIWsah\nRozEyc7UIfXIV/s05iTrMlU0Onwxvduq8rn4EJtuTaUSdPuHmC+ow3J3BHL1VrNv\nqNSH6MECgYEAy/Pkz1TqXOTGfeU/TFmS5cTA5tMTajfhdXci91/apTIrWo8tsRZJ\n5sKx4rj/5rN/W3ZTRYrrAtIGAC8ClBXS57D4t4dcZU2Ga0cDAV/6awYWcikxr9+X\n6XVkcVnH5jqyGVPSgLFr/LFUVGyM4qep31uSy5ffG/k5RxM88wP3hXUCgYEAw5oE\ny709VzAJ5JObvik5iB3xmjHWE5Jzo7hzYfFbCP6mwhBYhc6eXcsa9UYRJWOX5H2Y\nU0ZcmeETUzR2frsYCSw3Z4oLz3A93xsrwQIIQSz41CsiqoPCjT2hLxIYmYuWz6VU\nMyfzIyvJDgrXeRde2SyUE2ml0ZJnTeMh1mFoFgUCgYEAsX9VRsuUG7xOswpsiDn8\nilMtvrU8VFjFssE4gSm+075R1MJ+9Xt8XYRb5AE1VMYqtKLJ3eAEn7PA3TAMgmxr\n3JA8JR183/0UWxC2IOAyxCnMJxaK05E4WEl+XNfSDSBQF5LHxJCkdoqt5buC8U8R\nm/3KR72owmOn4Z9wj96H8HECgYEAhBnv5T2AQPMDq2j5RRuIbGJ0ukOwJfXBva+j\n1WDwr4l0TBpH/s6FbCjwovfYOp0hh0I/bvZVMeOtboM/B+YJnKBNJtM3mLhgQN6T\n1T0vH/1ka75aIjjsWwFla2nYTVLV3pT04bu4XGeE2MP/tBtRhnCx7M5sG7a5qAOe\n5a9RYZUCgYEAkau/ejzNmmzRLaPIKpgw/eOQP+sIRb4jLFpC2cbG77vnIf7fVj0r\nhu1YR0QfrPMlEGENCJctCOz4naNQ02Mk5RSGr2h2kBCnG9Vw54iELS21GYGBI3Fd\nGjTWe1T4PgNcARZ7VMc8+9H7VljMRAg/5qCaAQGa9sGCkNucMJyMNyM=\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArgqisCEAG+Gi3LfPCg7ckzhHO1Olp8khpzk5N3Hy9I+ugplw\nBtvzhCrWQ4Pm8Flehn7XZ0l/5113bIZu15vphwqvCmy5/BUmtwrc4wgsop5+pfgw\nHprKNYQPRo+Ji1AP3IHv3bmMBrwAx/mxMOuSyebunSxLftEmePGUgzkVwCbt6CbP\nWnlD5Pxu2qemo37eX9Gbt4CpDPT0XDK7VuX3Wp/nmHHcmUl/TGEupF7aeuRvgtMe\nXPrp49FvfZsOtcFd6WB/vSCC7BPzNNDknZdIxSXrMB6BUtZC/V/FFT1jGJqNY7I5\nOnwt7lSgxJpBFJzpq/ksP46mBy1dgomzMxNdxQIDAQABAoIBAHrbaSK5+PC1pBj4\n6/8mpJCMPsRjdOHveoEoRPqdxqrbLo5ksA83fzyCXMRGPWTXQYHh0j7IRLDXavFC\nKMas+fnpEc1jGRsY9z5pPapKX+/fwHl6rMU98ZY7hekCdE7Sko/PbbrwOyeZ2VdR\nLMLi2LkL+s8asKRyEh14M6U6S99asbii0M3SBZRcRXlp19Ak2tmemc2mSqb/7r3q\nxm31ZPpwavVYQMqruUoG3TQQ5rXRqEejw3nfD4MXf5I5qWpM75cu/S6d1CuYlOz+\nA0UEYSzzWklp+4Wy3Gm0sVoFvefmJ8zbCKZ+HVhKrJ16MGCma33vQVo8IP9wOggr\n6/mx8HUCgYEA1aE8Zx86vxqELaSsDC6tNFdhPNa2t2KmzHeBXOOVGPJO0fRz73N+\nGF+ARLZJ/PvTvBr329wCIdk4lZOzSpGwA59xl3vy4URGcuqGbVgI4NkZmbZ2aYEQ\niaY25Z7ZP40n9d4E7AldE4ucNeJk+1w27WLEPIGHqalr6zwPwdRQPIMCgYEA0I9b\nuhYN0i0HdvXUjTbsFkoPJhi9BBF+BGbbO+2MUP4DtM9OvlhUcmirYAiZDA1JiotJ\nqkWBRq6bVnFh91lN7eyOcDYYLGWF3Es59TGU/WYZb+OZ3dcgckfXlZRmNXQP9/NF\nOovcyLUugpmGjk+JxTa8shyy2hrNunP+7jlC+hcCgYAX8uplmR+p2twkjchn2Te7\nnWweOOfk6R9P3rnW4wCM+mURrnjsyCLDsrkbeuASh1y2QsO0lxZ6GvL3+cXovypf\nTZmbZN7WmCPZsCb/zRW5tzzieY0OyNREyihvV359XGK1cn1UxLv1e/o4JDgz4aOa\nuy7Kpa7Cu2aIyYPus9GG5wKBgE3RP7N2KSADxyY+4VjzZjGG3pIjZttOv4ta6XA5\n2UIbFTzwoPvqr0+k+FSzwI87ofX8tLbAilTaL4GYeqo0+xvMSPVbabefBcxFkVGI\nd4P5BYK3FKEudJ/PaIQaQ+yr6o1kiq8monGNENaP/CG7UbdxDgUTKjSxDZQFVdiD\nKxdvAoGBAMFaoifoPpgM2IkpsarKhLzfIIXAPHmerqCdto/aga2OhJmp/JzbQW4o\nvwcuWfbFEWLeNb4w7607XHvI3RzpzpFD6mkrYYYclKk0UOcfwe7o/QJ+gEGcmbS8\nn2ekqO/xx8pYSFFBxwKPkRkXIMSwM7YXgrjw8clI9qYwmzZSw+uC\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0+rSeoCIUFUQ6STSA9RaFUWGWx5OvvWbY8efI70909eRKCyp\niPWtRsOjLAl5JaiZRxT7IWGX+5b8Jw2JUzKSNY/LG70HtaoSKMxjtLfnBv3QTk02\nW2K8/Yv8HfLEUlabku7prfAcMt0Dog8gvEhuep0mNFKwaML5hXEu8lShIlaGYpA0\n07lTWvEawwjBk1axKhKWvxcc9H+Hi+Hf7+4agwemjT5JuihDEPxXzsZzP0LzxSno\n8/AwWLOQLOXs4jqJt0+6Nunaj4LmRVYS0kYIphyQa3xxJrIEPJ2VezuIO1hPGBxS\nZw1LyCSBX6P2mBujm7zZxowWMl+nHG8sGs4cPQIDAQABAoIBAQDAwIP1vNdACj+5\ncC4+IXZWhshwNJ73fjp6hWWwgj6sYxS67/S8iIm1JdlTAsLYiAPy7ZiGglhVFL4B\nX4Sa0hK++S2K62MJz8l4pn/CLsvKRi6kKgLZGHRCQoNtUhVTt0cGXJzcAtwDP5hA\nXXYvC8rBaBMJ4HRotX9z6sEC1gtCSL1qm/kRdqy2I3qGLMMYnfHJYhFb1qG5FOhH\nf8SFBql0FaSEtYnIUOi+Mui7OAM53oBQtKp7HRFfMZBsq5n2e46K0sHPSsvwCQqd\n0p3bB7x6NQaI1orJOzoyUjGO3aMjbuV/GgcpOIDUKA9+LBmOpeu54nXBtvUD9+Nx\ngi/gZ6fBAoGBAOynx6QNxDDGqZpuYb+xP0hOwZcNWFMNTM25cZ0W1x6nWIftEnsP\nPDYJlAQC3MhGbNLC+QIHDaPOgnGmXsyW6XKvON2CiPzb2+ZSGcCIuA6NF7PTQynC\nlS0Hc7x8VA6HtX719J6/jiduP5OHidsir2JLGtz4AtSuK69HiGDmMnUFAoGBAOU9\nYC7MkoN4VoMmP4lucUa57B/epkKKBSvz/NKcKWoCzZDMxj82S6KYikSIdXjE1IoF\n5+oy4UaUeBWkryy47NU2EfJHxj9Jy5huCcaNP178fOjEHBgx0ICsfbRDj5LqNhz4\n2hJg+PQK+SQRCF8uV1j2Nr6p1xZ43rkiIwpx6S/ZAoGAPX0KfjGlfBbiEmIDu87N\n5newDRNGk62s+vbn8izxD+HjOs36M0cuGcw7TR+BRfgmZkyKbmBuxtTtR6I2l2nO\nliG44LT96tUlOZ6zWjcru3wlYG/Pz5XjNKPvClYzcOMJ4Ub+nBChWtVmZ2qcAvfs\nIdnpzOgTtDbc2tn3MVYeXhECgYBNIMNVu5qoBTsGUT4bXT9bjn51kpHZ3Vo0GZk8\nuzHr6xmC2ILzDa+Q+0W6HTd49LCV57rJv2iwOI/WqFwP7gAxFEohfCilbozDsnIz\njZ5+tPDJ2AGj2k7OnDqHBhwuUAL46HlmqZ3Zbj+49MoeTBczZVOx/q7RbsVxAJ1N\njFnqsQKBgQCeV+lz+7xSvHTu8ZAJPRjisDLgcv3IcmhiMbdaPiYmp4UtWaXTSBfH\nwUuChkW8HDHOq+0Gn/RusEPpE2eufYzCXqWWheKhr+4YC23yneHIMu+sMbaVeMEk\nnClfBIjnIUFteX1kKS7uhKLnpMhBfdueHBWfPihltpDdplVBv8wA1g==\n-----END RSA PRIVATE KEY-----\n', b'-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAvxfK497pXrc5hcC18EHaPi1DDCNbamIZUqwg8EUcUDg+jkMV\nhSuy0MZgYopQzkj4oUblvInywM02N081KfL8VfA7WGc4vclbc9xZk5RJtQeTveeG\nmN6pTUIJwlKcZco+jzSvVXfzTy3V5JocO+Bxawr2hrjCfREdq3DWb+n/0Zg8hC/P\nFwEh84sszU2nh/hp+jeP3WSQvgDTsDL4mE4TdXUkZ3jpSe0iaaGP50zIsp73MlJQ\nzdyuWmwsElWI6YMDmniNoaD2uV8lH0SaG0XiS8j5Od1UuvyMftQZQ2SWsmmmpaFd\nmpkp73VySZBSbcfL/r1NJbzWa8eBQNw8bUkTYwIDAQABAoIBAQCHaC2dkKwfMIgz\nLd3+NDuYiWwUIs25MXAuM8hXWTF56pDxL7oFFnTsGVXdHZOqyOxdI6bRFZMNaZx8\nTsUmR4bkwoja2LHNUexayg4zVAVyoL7g9zlaA5VGKIBP0Uf/nPL9eOCxGIExXxJM\nnaNaDm1v42HTDwLJqqIyhI9+1/otIpADwpVDucXyvcxuaPI+Ml4z6foyUyiu17Cd\nNWFkrwqNQAkIItoftTbcInFKOwg7+h39SwE2mzxStgybIIWNjgOuURqfj2XjPgkR\nkaVC22DJz+0pKaCEGy8JRKNfBNAh2tnGNwseKc7VqRoQwbyqP5Fk5cgme1x77oXg\ne7SlbfKZAoGBAOkfKtq8Fk2ZWUtoQogDG59ZiqEkjhFdyWl2dCBM5hEJIpc4uZHS\n7lAY4pL/NO2xyo7GW3fxu4/Hp3ITHykM/EmPoHqHzR03ci8fHC7G7b4YGo2EXMex\neCjXTcVG1ZPkUNz+K+3/L8bwEl8u5uWSFjJL4SBlz8qlbJqmfeP6Rq7vAoGBANHY\ntvTcUIW6ikNdFjBXujVu0B/X3GKOUPxEIZr/4CqelUvtzdU3IUWv1jzdnhGBn9WX\nVmT0NLpo1mdbQA0EfVOREPLSJwm2NSb/tRoZMi2T6TZ9Yo2XBDQtZQoXiUyPYUZo\n28rP4Y5aB/r0ReoxYs4dkQ9LXVO1DQwa8P8nwOLNAoGBAI7xLnzK4b4ZLnifaFQv\n26sc43Uljfs2D78cA96eaF+/wzcIcDjrJnd0mtFZrSGmZ7aeCA4G6mQiHxlCglPQ\ng42zhMo9JNcqXVhpcgV4suRhsT5fD+jed/hK9IPniCRT1gV70IHxfGE2aX11q9zO\nc8/JWTRdWUk/n9VAC2dT2i1/AoGAWyF/StVGztyU4O5TDYl7n2rRTZLj7ZUajINW\nCMcoZbna4k+SvQ6lD2tuYZ5o3/doH00Kwure3zlaKyaQqVaSyrDMKHelZm1PpfEU\niBH7HZoHhJdtLkowFCzGnVxdTvdRak9tbCVdMgHdR1m/5xWtcA+dCIRVMl4FHYWx\ntU1hUd0CgYAOjRXi3UBOiiWC7pTyfGSJ8gQ8W85C4j+b9xKi2iSJ+NNjYrGDKMBs\n2RZhTO1saQc70Va5AXu6i7FP3Wp7elePseY+Mn97jUeVH49JrTUdl19PbNc2LAWf\njupjye9PepN7Q8ORNXTap5uXpGw7n128aHO+gJ6IvpUY1sQtHWYHNg==\n-----END RSA PRIVATE KEY-----\n']
+
+ed25519_known_keys_serialized = [b'1aeed372554183f2a7e25e240e22289e81615f9dd211ddcdc3942d0226cf9e1c', b'e920aa4173352faea24b4b1964dac0d57bfd9906908048e46ac486307d5337b9', b'a7eb15c52a4159f9f7b4785fdb79e55b1644f7c7cfe246c5b35464b52f6c8e8e', b'1b90f22cd55dcde52d71846610f0f191feb5be9485c1c8f36191c3a44158cdfc', b'2a7a9a1ff1088f8eef3a39ce13ea08088af60678d53e0b9c84fb3993d6fea03d', b'426767c0badfb4d3f5c51f71978ab48e9aea3eecafdcc72b011b068f055663bc', b'8fac1502ceb4102756912bd057e76ce0c5466538f0c809e0b457fb11fc00e9df', b'9c024b5e6a83358a2a71704eab747222335a82d98e9c8c41626b0262bd5931cb', b'c200c62e45def239810af86d5329e31b8e57adfa291b071aee34e6575aebf21c', b'3e5f71f540bfa2e5bbaed49eb8fdcf8bb57a305380f91f0f4edd7b2e49cf7c12', b'8404dd346a4d40ef70f7ef7f25f4d0a0a8adc1511c16579d5ed9a645daff5e1e', b'563dfd5f56ef52a4bcc520998bc7f1c841cd936e4e7f95812aa16b4633d9115f', b'f8587264b9fdec47172fcebaba4accb9b49bfe0be2c7fce13fc7390f96df3ffa', b'0c453e20e212636a7c3675ad2ed7c039277e389aa2d33b2418f8684e7ffa5df3', b'559183d062bb44996efdc1aaeff48b055f39da5acda0c5d43b12e9c22b7d0cc7', b'0e2d4072237c1166dbe2a26e4a4e953a037862842b403ec5a8934db5e0e4c5dd', b'b7bd934b0339f86f47667fd44f9432e6b2709c2c6492c4b1dd046d009241e941', b'df538176ba4ed2e9b598fd534389a8977452485c423a2c94b6817cb600bdd29c', b'5973bb41b0e0cec2a985aa05a47e3b51098d3e47b775da8139a0e1d59fb09c5a', b'59de6042594ebcfb369214f554853a94c4942a151c81bded818fcc5106a4ae45', b'15e5d2b8151fe1897e3f333b27ed94b6e44782a0a840b94da865b8792b0f1b4c', b'6f78dc8f33c2310b537d72aee13a9b8d6f7788dc3fba1dc1785230f94f2076b8', b'974bf395dc9b3ebf54e15daf49f7ff3a0174d03c9a907abe9c869cd3ee05ed1d', b'c7ab870bb3ca9f0c87ed7b4ce289c23ce4aba623183faba5ae7d205f656d40a9', b'1dfa6928beae45fb237e3e40382dfa28d7ffb874f5524b3df499062930a6fa01', b'c19f3a86f00eecc41328d98ccde5cb17a6cd94366559823827017c580eebd763', b'15752c00d7c643eea1220bf05af5cdf6a2c0165d4c2a5c571ebf195ac7c9f95a', b'8c8eba92ae3f7de2f956d14c2639a67af9e78fac8c5432dcd188cb990704646f', b'82af783b9cdf92f013b626dff07537cda6a4d583722457bffd652c52faa4785d', b'5dc5400756fc83835164f9016c900e26b9c3d833af8b3ee3e86883a22006da98', b'4d3963403c356bdb18814e567106abf135c998da3ef799c9dcf908483ca5dce0', b'325e2f60747041201cf09320474955839967d5b67a95c9b4525b812ac970e7a0', b'69c9103a629930d9b47dbcc9a2a880ee123b4db4fd3f016763b38e7722553528', b'5f7051e7f9667ce4f96b4d2d86583ffa7fe7d91562f1e6756135496435c5faa1', b'b33f79a6672ca3bde7df7d7f998973fe703069b84cd94c30510c637576023882', b'd22b1dbc3ab13eaffabe5a9faa5e09a56f89531e27c3e0c535501daadc524a6a', b'21c726da4161983b2cf3e4802334ad5ea3a7b2d53329c98b4dd51add8e54234f', b'98f098b26e35bb2af0453715bbe1df7ad05bdb276adc68adb9178e145aa3524b', b'5caa2fd97856f18b642017b3f6f757e18e7675f6042d288b6135a22b9c8aeb1d', b'6e1b197476878c7d68e0e61b3670f9ae4e8166500e6e6b2c040edec58117ab39', b'c378998d2c87dd9591a54421b51314dc1fd3f0ddfc074be5d33c2b3d919bd757', b'03ada8dd8cc51885d6435bc0a4416f1594f2d35b193b16c052812e49e40704ee', b'8cb1349d69d48d0c639c2872c3fc0423ae992ef25457a0a222fb23d6d7308e73', b'78fa2ae2f5071d43939a414107af221e32571799529736e61f8baf7f78e630f7', b'2178249788af19a381410e4ccd56c89a75ea49a0bf164b70cef8809f839b5d0e', b'783c2d08e57a76968d52cba7370344527f100ef9255df06a95fc64ceff13e288', b'3acba399697e9b212919c372d852bff4db5ed3c77f8fd4be7debbfd4d88d025a', b'a78e9c3af9623707a2d3ea98d7c4ff21e810bd393f133d532f212adcbb48947e', b'5f3b59af31330365e46fd4117cc009a115c15543c749785430954e760a774b89', b'e0c8abbc27d16f78fe4399ff77fead4ce66db946cdf6f4a5fa9b03b2b8caf39b', b'b70416b241c30bbc1e20b1519978600cc710d723937f4ba46a6cde808a1165cc', b'd79d5b510b89099049b671193531617dfa87ed8f4d261c20e862c07c6d4f34c5', b'f88205a87c6e3fe18b5439b757466692e67eb901feb7f89087b39c03e7b2cbfa', b'efe41b082f1f11cac72b74ae4cd5cfaff4aa21c9efc2b1e45c71108f07647083', b'6baaaef2ad721cd28fd3b5711b66058e87e564d38a86cd42b09dfa88e927e04a', b'2c1162689b1aeddaef68198400a5e0f3f23b871506c715924b9852dc8554b782', b'85bf0fba2f748b78bebb0e69f2c1a719e26700759e2f6d5095d685710d6c0e68', b'88fb2e6b887ab9f0cb452948632cba6caca595bcebe263f7acf8ae56551ee5ef', b'df71b8904f33fb061c4fde5228e2fd922c503b5b76ab46de401de4cd046ea30f', b'bd44dc415121c9498fe1f4e5942904fdd9ecb879ad5f772d6a1e84b55450ab77', b'bab6ab52f6a5614641e4e338a8278e55f0d7e7ba1689b30a6492e0ff03c5dd85', b'8422190a931d3ec275e8b49bf062f8bbef52e814d1fd3b0a6c0d8f901e9d4ded', b'e6403bb2a179d6dc3483aca326ae911153acd439af74e99afb4638a355ebcd4d', b'581e4c3008f42cffc658d6c6b13f3f430d50f56cd30e91fa23ff08f0bf5e5472']
\ No newline at end of file
diff --git a/docs/source.dox b/docs/source.dox
index d0d82d7a86d..e8afed2b6ab 100644
--- a/docs/source.dox
+++ b/docs/source.dox
@@ -124,6 +124,7 @@ INPUT = \
../src/ripple/app/tx/applySteps.h \
../src/ripple/app/tx/impl/InvariantCheck.h \
../src/ripple/app/consensus/RCLValidations.h \
+ ../src/ripple/conditions/impl/Der.h \
INPUT_ENCODING = UTF-8
FILE_PATTERNS =
diff --git a/src/fuzzers/Conditions_fuzz_test.cpp b/src/fuzzers/Conditions_fuzz_test.cpp
new file mode 100644
index 00000000000..5546a494df5
--- /dev/null
+++ b/src/fuzzers/Conditions_fuzz_test.cpp
@@ -0,0 +1,66 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+/*
+ Note: This file requires clang 5.0 or greater.
+ Typical build setup:
+ cmake -Dfuzzer_conditions=ON -Dsan='fuzzer;address' -Dtarget=clang.debug
+ Typical run:
+ ./fulfillment_fuzzer -max_len=5000 -jobs=4
+ An initial fuzz corpus may be generated with the `conditions.py` script.
+ */
+
+
+#include
+#include
+
+#include
+
+#include
+
+extern "C" int
+LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
+{
+ using namespace ripple;
+ using namespace ripple::cryptoconditions;
+ using namespace ripple::cryptoconditions::der;
+ Decoder decoder(Slice{data, size}, TagMode::automatic);
+
+#if FUZZ_TEST_FULFILLMENT
+ std::unique_ptr f;
+ decoder >> f >> eos;
+#elif FUZZ_TEST_CONDITION
+ Condition c;
+ decoder >> c >> eos;
+#else
+#error("Must define either FUZZ_TEST_CONDITION or FUZZ_TEST_FULFILLMENT")
+#endif
+
+ if (decoder.ec() == Error::logicError)
+ {
+ static boost::filesystem::path model{"logic_error%%%%.dat"};
+ auto const fileName = boost::filesystem::unique_path(model);
+ std::ofstream ofs;
+ ofs.open(fileName.native(), std::ofstream::out | std::ofstream::binary);
+ ofs.write((char const*) data, size);
+ ofs.close();
+ }
+
+ return 0;
+}
diff --git a/src/ripple/app/tx/impl/Escrow.cpp b/src/ripple/app/tx/impl/Escrow.cpp
index 3b9abf0149b..a14cf926930 100644
--- a/src/ripple/app/tx/impl/Escrow.cpp
+++ b/src/ripple/app/tx/impl/Escrow.cpp
@@ -171,7 +171,7 @@ EscrowCreate::preflight (PreflightContext const& ctx)
std::error_code ec;
auto condition = Condition::deserialize(*cb, ec);
- if (!condition)
+ if (ec)
{
JLOG(ctx.j.debug()) <<
"Malformed condition during escrow creation: " << ec.message();
@@ -180,7 +180,7 @@ EscrowCreate::preflight (PreflightContext const& ctx)
// Conditions other than PrefixSha256 require the
// "CryptoConditionsSuite" amendment:
- if (condition->type != Type::preimageSha256 &&
+ if (condition.type != Type::preimageSha256 &&
!ctx.rules.enabled(featureCryptoConditionsSuite))
return temDISABLED;
}
@@ -281,14 +281,14 @@ checkCondition (Slice f, Slice c)
std::error_code ec;
auto condition = Condition::deserialize(c, ec);
- if (!condition)
+ if (ec)
return false;
auto fulfillment = Fulfillment::deserialize(f, ec);
if (!fulfillment)
return false;
- return validate (*fulfillment, *condition);
+ return validate (*fulfillment, condition);
}
TER
diff --git a/src/ripple/basics/Slice.h b/src/ripple/basics/Slice.h
index 076dbb0acc5..5e168994cef 100644
--- a/src/ripple/basics/Slice.h
+++ b/src/ripple/basics/Slice.h
@@ -22,6 +22,9 @@
#include
#include
+
+#include
+
#include
#include
#include
@@ -34,32 +37,53 @@
namespace ripple {
-/** An immutable linear range of bytes.
+/** A linear range of bytes.
A fully constructed Slice is guaranteed to be in a valid state.
A Slice is lightweight and copyable, it retains no ownership
of the underlying memory.
*/
-class Slice
+template
+class SliceImpl
{
private:
- std::uint8_t const* data_ = nullptr;
+ using TData = typename std::conditional::type;
+ using TVoidData = typename std::conditional::type;
+ TData data_ = nullptr;
std::size_t size_ = 0;
public:
/** Default constructed Slice has length 0. */
- Slice() noexcept = default;
+ SliceImpl() noexcept = default;
- Slice (Slice const&) noexcept = default;
- Slice& operator= (Slice const&) noexcept = default;
+ SliceImpl (SliceImpl const&) noexcept = default;
+ SliceImpl& operator= (SliceImpl const&) noexcept = default;
/** Create a slice pointing to existing memory. */
- Slice (void const* data, std::size_t size) noexcept
- : data_ (reinterpret_cast(data))
+ SliceImpl (TVoidData data, std::size_t size) noexcept
+ : data_ (reinterpret_cast(data))
, size_ (size)
{
}
+ /** Can convert from a mutable slice to a non-mutable slice */
+ template>::value>>
+ SliceImpl (/*SliceImpl*/T const& rhs) noexcept
+ :SliceImpl{rhs.data(), rhs.size()}
+ {
+ }
+
+ /** Can assign from a mutable slice to a non-mutable slice */
+ template>::value>>
+ SliceImpl& operator= (/*SliceImpl*/T const& rhs) noexcept
+ {
+ data_ = rhs.data();
+ size_ = rhs.size();
+ return *this;
+ }
+
/** Return `true` if the byte range is empty. */
bool
empty() const noexcept
@@ -81,7 +105,7 @@ class Slice
@note The return type is guaranteed to be a pointer
to a single byte, to facilitate pointer arithmetic.
*/
- std::uint8_t const*
+ TData
data() const noexcept
{
return data_;
@@ -97,7 +121,7 @@ class Slice
/** Advance the buffer. */
/** @{ */
- Slice&
+ SliceImpl&
operator+= (std::size_t n)
{
if (n > size_)
@@ -107,15 +131,30 @@ class Slice
return *this;
}
- Slice
+ SliceImpl
operator+ (std::size_t n) const
{
- Slice temp = *this;
+ SliceImpl temp = *this;
return temp += n;
}
/** @} */
+
+ template::value>>
+ void
+ push_back(/*std::uint8_t*/T v)
+ {
+ assert(size_ != 0);
+ *data_ = v;
+ *this += 1;
+ }
};
+/** An immutable linear range of bytes. */
+using Slice = SliceImpl;
+/** A mutable linear range of bytes. */
+using MutableSlice = SliceImpl;
+
//------------------------------------------------------------------------------
template
@@ -185,6 +224,17 @@ makeSlice (std::vector const& v)
return Slice(v.data(), v.size());
}
+template
+std::enable_if_t<
+ std::is_same::value ||
+ std::is_same::value,
+ Slice
+>
+makeSlice (boost::container::small_vector const& v)
+{
+ return Slice(v.data(), v.size());
+}
+
template
Slice
makeSlice (std::basic_string const& s)
diff --git a/src/ripple/conditions/Condition.h b/src/ripple/conditions/Condition.h
index 0c92a5ea95f..ecb493ef6c7 100644
--- a/src/ripple/conditions/Condition.h
+++ b/src/ripple/conditions/Condition.h
@@ -22,27 +22,19 @@
#include
#include
-#include
+#include
+#include
+
+#include
+
#include
-#include
-#include
-#include
+#include
#include
-#include
+#include
namespace ripple {
namespace cryptoconditions {
-enum class Type
- : std::uint8_t
-{
- preimageSha256 = 0,
- prefixSha256 = 1,
- thresholdSha256 = 2,
- rsaSha256 = 3,
- ed25519Sha256 = 4
-};
-
class Condition
{
public:
@@ -57,7 +49,7 @@ class Condition
/** Load a condition from its binary form
- @param s The buffer containing the fulfillment to load.
+ @param s The slice containing the condition to load.
@param ec Set to the error, if any occurred.
The binary format for a condition is specified in the
@@ -66,9 +58,12 @@ class Condition
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2
*/
static
- std::unique_ptr
+ Condition
deserialize(Slice s, std::error_code& ec);
+ static
+ bool
+ isCompoundCondition(Type t);
public:
Type type;
@@ -77,36 +72,64 @@ class Condition
This fingerprint is meant to be unique only with
respect to other conditions of the same type.
*/
- Buffer fingerprint;
+ std::array fingerprint;
/** The cost associated with this condition. */
std::uint32_t cost;
/** For compound conditions, set of conditions includes */
- std::set subtypes;
-
- Condition(Type t, std::uint32_t c, Slice fp)
- : type(t)
- , fingerprint(fp)
- , cost(c)
+ std::bitset<5> subtypes;
+
+ Condition(
+ Type t,
+ std::uint32_t c,
+ std::array const& fp,
+ std::bitset<5> const& s = {})
+ : type(t), fingerprint(fp), cost(c), subtypes(s)
{
}
- Condition(Type t, std::uint32_t c, Buffer&& fp)
- : type(t)
- , fingerprint(std::move(fp))
- , cost(c)
+ Condition(Condition const&) = default;
+ Condition(Condition&&) = default;
+
+ ~Condition() = default;
+
+
+ // A default constructor is needed to serialize a vector on conditions - as
+ // needed for the threshold condition.
+ Condition() = default;
+
+ /// Construct for DER serialization
+ explicit
+ Condition(der::Constructor const&);
+
+ template
+ void
+ withTuple(F&& f, der::TraitsCache& traitsCache)
{
+ if (isCompoundCondition(type))
+ f(std::tie(fingerprint, cost, subtypes));
+ else
+ f(std::tie(fingerprint, cost));
}
- ~Condition() = default;
+ template
+ void
+ withTuple(F&& f, der::TraitsCache& traitsCache) const
+ {
+ const_cast(this)->withTuple(
+ std::forward(f), traitsCache);
+ }
- Condition(Condition const&) = default;
- Condition(Condition&&) = default;
+ /** Return the subtypes that this type depends on, including this type.
- Condition() = delete;
+ @see {@link #subtypes}
+ */
+ std::bitset<5>
+ selfAndSubtypes() const;
};
+/// compare two conditions for equality
inline
bool
operator== (Condition const& lhs, Condition const& rhs)
@@ -118,6 +141,7 @@ operator== (Condition const& lhs, Condition const& rhs)
lhs.fingerprint == rhs.fingerprint;
}
+/// compare two conditions for inequality
inline
bool
operator!= (Condition const& lhs, Condition const& rhs)
@@ -125,8 +149,61 @@ operator!= (Condition const& lhs, Condition const& rhs)
return !(lhs == rhs);
}
-}
+/** DerCoderTraits for Condition
-}
+ Condition will be coded in ASN.1 as a choice. The actual
+ choice will depend on if the condition is a compound condition
+ or not.
+
+ @see {@link #DerCoderTraits}
+*/
+namespace der {
+template <>
+struct DerCoderTraits
+{
+ constexpr static GroupType
+ groupType()
+ {
+ return GroupType::choice;
+ }
+ constexpr static ClassId classId(){return ClassId::contextSpecific;}
+ static boost::optional const&
+ tagNum()
+ {
+ static boost::optional tn;
+ return tn;
+ }
+ static std::uint8_t
+ tagNum(Condition const& f)
+ {
+ return static_cast(f.type);
+ }
+ constexpr static bool primitive(){return false;}
+
+ static void
+ encode(Encoder& encoder, Condition const& c);
+
+ static
+ void
+ decode(Decoder& decoder, Condition& v);
+
+ static
+ std::uint64_t
+ length(
+ Condition const& v,
+ boost::optional const& parentGroupType,
+ TagMode encoderTagMode,
+ TraitsCache& traitsCache);
+
+ static
+ int
+ compare(
+ Condition const& lhs,
+ Condition const& rhs,
+ TraitsCache& traitsCache);
+};
+} // der
+} // cryptoconditions
+} // ripple
#endif
diff --git a/src/ripple/conditions/Fulfillment.h b/src/ripple/conditions/Fulfillment.h
index c0296419313..68b950d6159 100644
--- a/src/ripple/conditions/Fulfillment.h
+++ b/src/ripple/conditions/Fulfillment.h
@@ -23,14 +23,22 @@
#include
#include
#include
-#include
+#include
+
#include
+#include
+#include
+#include
+
namespace ripple {
namespace cryptoconditions {
struct Fulfillment
{
+ friend class ConditionsTestBase;
+ friend class ThresholdSha256;
+ friend class PrefixSha256;
public:
/** The largest binary fulfillment we support.
@@ -39,11 +47,11 @@ struct Fulfillment
that were previously considered valid to no longer
be allowed.
*/
- static constexpr std::size_t maxSerializedFulfillment = 256;
+ static constexpr std::size_t maxSerializedFulfillment = 4096;
/** Load a fulfillment from its binary form
- @param s The buffer containing the fulfillment to load.
+ @param s The slice containing the fulfillment to load.
@param ec Set to the error, if any occurred.
The binary format for a fulfillment is specified in the
@@ -57,6 +65,37 @@ struct Fulfillment
Slice s,
std::error_code& ec);
+protected:
+ /** encode the contents used to calculate a fingerprint
+
+ @note Most cryptoconditions (excepting preimage) calculate their
+ fingerprints by encoding into a ans.1 DER format and hashing the
+ contents of that encoding. This function encodes the contents that will be
+ hashed. It does not encode the hash itself.
+ */
+ virtual
+ void
+ encodeFingerprint(der::Encoder&) const = 0;
+
+ /** FOR TEST CODE ONLY return true if the fulfillment is equal to the given
+ fulfillment. Non-test code should use operator==
+
+ @note This uses an inefficient algorithm for comparison. Threshold is
+ particular problematic. This should be used for TESTING ONLY!!!
+ */
+ virtual
+ bool
+ checkEqualForTesting(Fulfillment const& rhs) const = 0;
+
+ /** FOR TEST CODE ONLY return true if the fulfillment depends on the message.
+
+ @note Preimage does not depend on the message. So any fulfillment where
+ all the "leaf" fulfillments are preimage would not depend on the
+ message, all others would.
+ */
+ virtual
+ bool
+ validationDependsOnMessage() const = 0;
public:
virtual ~Fulfillment() = default;
@@ -68,8 +107,8 @@ struct Fulfillment
same type.
*/
virtual
- Buffer
- fingerprint() const = 0;
+ std::array
+ fingerprint(std::error_code& ec) const = 0;
/** Returns the type of this condition. */
virtual
@@ -91,28 +130,79 @@ struct Fulfillment
std::uint32_t
cost() const = 0;
+ /** Returns the subtypes that this fulfillment depends on.
+
+ @note This never including the current type, even if the current type
+ recursively depends on itself (i.e. a prefix that has a prefix as a
+ subcondition will not include the prefix type as a subtype. @see {@link
+ #selfAndSubtypes}
+ */
+ virtual
+ std::bitset<5>
+ subtypes() const = 0;
+
+ /** Return the subtypes that this type depends on, including this type.
+
+ @see {@link #subtypes}
+ */
+ std::bitset<5>
+ selfAndSubtypes() const;
+
/** Returns the condition associated with the given fulfillment.
This process is completely deterministic. All implementations
will, if compliant, produce the identical condition for the
same fulfillment.
*/
- virtual
Condition
- condition() const = 0;
+ condition(std::error_code& ec) const;
+
+ /// serialize the fulfillment into the ASN.1 DER encoder
+ virtual
+ void
+ encode(der::Encoder&) const = 0;
+
+ /// deserialize from the ASN.1 decoder into this object
+ virtual
+ void
+ decode(der::Decoder&) = 0;
+
+ /// return the size in bytes of the content when encoded (does not include the size of the preamble)
+ virtual
+ std::uint64_t
+ derEncodedLength(
+ boost::optional const& parentGroupType,
+ der::TagMode encoderTagMode,
+ der::TraitsCache& traitsCache) const = 0;
+
+ /** compare two fulfillments for sorting in a DER set
+
+ @return <0 if less, 0 if equal, >0 if greater
+ */
+ virtual
+ int
+ compare(Fulfillment const& rhs, der::TraitsCache& traitsCache) const = 0;
};
+/// compare two fulfillments for equality
inline
bool
operator== (Fulfillment const& lhs, Fulfillment const& rhs)
{
- // FIXME: for compound conditions, need to also check subtypes
- return
- lhs.type() == rhs.type() &&
+ std::error_code ec1, ec2;
+ auto const result =
+ lhs.selfAndSubtypes() == rhs.selfAndSubtypes() &&
lhs.cost() == rhs.cost() &&
- lhs.fingerprint() == rhs.fingerprint();
+ lhs.fingerprint(ec1) == rhs.fingerprint(ec2);
+ if (ec1 || ec2)
+ {
+ // can not compare if there is an error encoding the fingerprint
+ return false;
+ }
+ return result;
}
+/// compare two fulfillments for inequality
inline
bool
operator!= (Fulfillment const& lhs, Fulfillment const& rhs)
@@ -160,7 +250,66 @@ validate (
Fulfillment const& f,
Condition const& c);
-}
-}
+
+/** DerCoderTraits for std::unique_ptr
+
+ std::unique_ptr will be coded in ASN.1 as a choice. The actual
+ choice will depend on the concrete type of the Fulfillment (preimage,
+ prefix, ect...)
+
+ @see {@link #DerCoderTraits}
+*/
+namespace der {
+template <>
+struct DerCoderTraits>
+{
+ constexpr static GroupType
+ groupType()
+ {
+ return GroupType::choice;
+ }
+ constexpr static ClassId classId(){return ClassId::contextSpecific;}
+ static boost::optional const&
+ tagNum()
+ {
+ static boost::optional tn;
+ return tn;
+ }
+ static std::uint8_t
+ tagNum(std::unique_ptr const& f)
+ {
+ assert(f);
+ return static_cast(f->type());
+ }
+ constexpr static bool primitive(){return false;}
+
+ static void
+ encode(Encoder& encoder, std::unique_ptr const& f);
+
+ static
+ void
+ decode(Decoder& decoder, std::unique_ptr& v);
+
+ static
+ std::uint64_t
+ length(
+ std::unique_ptr const& v,
+ boost::optional const& parentGroupType,
+ TagMode encoderTagMode, TraitsCache& traitsCache);
+
+ static
+ int
+ compare(
+ std::unique_ptr const& lhs,
+ std::unique_ptr const& rhs,
+ TraitsCache& traitsCache)
+ {
+ return lhs->compare(*rhs, traitsCache);
+ }
+};
+
+} // der
+} // cryptconditions
+} // ripple
#endif
diff --git a/src/ripple/conditions/Types.h b/src/ripple/conditions/Types.h
new file mode 100644
index 00000000000..e8ee1541d1d
--- /dev/null
+++ b/src/ripple/conditions/Types.h
@@ -0,0 +1,42 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2016 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_TYPES_H
+#define RIPPLE_CONDITIONS_TYPES_H
+
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+enum class Type
+ : std::uint8_t
+{
+ preimageSha256 = 0,
+ prefixSha256 = 1,
+ thresholdSha256 = 2,
+ rsaSha256 = 3,
+ ed25519Sha256 = 4,
+ last = 4
+};
+
+} // cryptoconditions
+} // ripple
+
+#endif
diff --git a/src/ripple/conditions/impl/Condition.cpp b/src/ripple/conditions/impl/Condition.cpp
index 9611507948e..3f3688bae76 100644
--- a/src/ripple/conditions/impl/Condition.cpp
+++ b/src/ripple/conditions/impl/Condition.cpp
@@ -17,225 +17,230 @@
*/
//==============================================================================
-#include
#include
-#include
+#include
#include
-#include
-#include
-#include
-#include
-#include
namespace ripple {
namespace cryptoconditions {
-namespace detail {
-// The binary encoding of conditions differs based on their
-// type. All types define at least a fingerprint and cost
-// sub-field. Some types, such as the compound condition
-// types, define additional sub-fields that are required to
-// convey essential properties of the cryptocondition (such
-// as the sub-types used by sub-conditions in the case of
-// the compound types).
-//
-// Conditions are encoded as follows:
-//
-// Condition ::= CHOICE {
-// preimageSha256 [0] SimpleSha256Condition,
-// prefixSha256 [1] CompoundSha256Condition,
-// thresholdSha256 [2] CompoundSha256Condition,
-// rsaSha256 [3] SimpleSha256Condition,
-// ed25519Sha256 [4] SimpleSha256Condition
-// }
-//
-// SimpleSha256Condition ::= SEQUENCE {
-// fingerprint OCTET STRING (SIZE(32)),
-// cost INTEGER (0..4294967295)
-// }
-//
-// CompoundSha256Condition ::= SEQUENCE {
-// fingerprint OCTET STRING (SIZE(32)),
-// cost INTEGER (0..4294967295),
-// subtypes ConditionTypes
-// }
-//
-// ConditionTypes ::= BIT STRING {
-// preImageSha256 (0),
-// prefixSha256 (1),
-// thresholdSha256 (2),
-// rsaSha256 (3),
-// ed25519Sha256 (4)
-// }
-
-constexpr std::size_t fingerprintSize = 32;
-
-std::unique_ptr
-loadSimpleSha256(Type type, Slice s, std::error_code& ec)
+bool
+Condition::
+isCompoundCondition(Type t)
{
- using namespace der;
-
- auto p = parsePreamble(s, ec);
-
- if (ec)
- return {};
-
- if (!isPrimitive(p) || !isContextSpecific(p))
+ static_assert(Type::last == Type::ed25519Sha256, "Add new case");
+ switch (t)
{
- ec = error::incorrect_encoding;
- return {};
+ case Type::preimageSha256:
+ case Type::rsaSha256:
+ case Type::ed25519Sha256:
+ return false;
+ case Type::prefixSha256:
+ case Type::thresholdSha256:
+ return true;
}
+ assert(0);
+ return false; // silence compiler warning
+}
- if (p.tag != 0)
+namespace der {
+void
+DerCoderTraits::
+encode(
+ Encoder& encoder,
+ Condition const& c)
+{
+ cryptoconditions::der::withTupleEncodeHelper(c, encoder);
+}
+
+void
+DerCoderTraits::
+decode(
+ Decoder& decoder,
+ Condition& v)
+{
+ if (decoder.parentSlice().size() > Condition::maxSerializedCondition)
{
- ec = error::unexpected_tag;
- return {};
+ decoder.ec_ = der::Error::largeSize;
+ return;
}
- if (p.length != fingerprintSize)
+ auto const parentTag = decoder.parentTag();
+ if (!parentTag)
{
- ec = error::fingerprint_size;
- return {};
+ decoder.ec_ = make_error_code(Error::logicError);
+ return;
}
- Buffer b = parseOctetString(s, p.length, ec);
-
- if (ec)
- return {};
-
- p = parsePreamble(s, ec);
-
- if (ec)
- return {};
-
- if (!isPrimitive(p) || !isContextSpecific(p))
+ if (parentTag->classId != classId())
{
- ec = error::malformed_encoding;
- return{};
+ decoder.ec_ = make_error_code(Error::preambleMismatch);
+ return;
}
- if (p.tag != 1)
+ if (parentTag->tagNum > static_cast(Type::last))
{
- ec = error::unexpected_tag;
- return {};
+ decoder.ec_ = make_error_code(Error::preambleMismatch);
+ return;
}
- auto cost = parseInteger(s, p.length, ec);
+ v.type = static_cast(parentTag->tagNum);
+ cryptoconditions::der::withTupleDecodeHelper(v, decoder);
- if (ec)
- return {};
+ if (decoder.ec_)
+ return;
- if (!s.empty())
+ if (v.type == Type::preimageSha256 &&
+ v.cost > PreimageSha256::maxPreimageLength)
{
- ec = error::trailing_garbage;
- return {};
+ decoder.ec_ = der::Error::preimageTooLong;
}
+}
- switch (type)
- {
- case Type::preimageSha256:
- if (cost > PreimageSha256::maxPreimageLength)
- {
- ec = error::preimage_too_long;
- return {};
- }
- break;
+std::uint64_t
+DerCoderTraits::
+length(
+ Condition const& v,
+ boost::optional const& parentGroupType,
+ TagMode encoderTagMode,
+ TraitsCache& traitsCache)
+{
+ if (auto cached = traitsCache.length(&v))
+ return *cached;
- default:
- break;
+ auto const l = cryptoconditions::der::withTupleEncodedLengthHelper(
+ v, parentGroupType, encoderTagMode, traitsCache);
+ if (encoderTagMode == TagMode::automatic)
+ {
+ traitsCache.length(&v, l);
+ return l;
}
-
- return std::make_unique(type, cost, std::move(b));
-}
-
+ auto const result = 1 + l + contentLengthLength(l);
+ traitsCache.length(&v, result);
+ return result;
}
-std::unique_ptr
-Condition::deserialize(Slice s, std::error_code& ec)
+int
+DerCoderTraits::
+compare(
+ Condition const& lhs,
+ Condition const& rhs,
+ TraitsCache& traitsCache)
{
- // Per the RFC, in a condition we choose a type based
- // on the tag of the item we contain:
- //
- // Condition ::= CHOICE {
- // preimageSha256 [0] SimpleSha256Condition,
- // prefixSha256 [1] CompoundSha256Condition,
- // thresholdSha256 [2] CompoundSha256Condition,
- // rsaSha256 [3] SimpleSha256Condition,
- // ed25519Sha256 [4] SimpleSha256Condition
- // }
- if (s.empty())
+ // compare types
+ if (lhs.type != rhs.type)
{
- ec = error::buffer_empty;
- return {};
+ if (lhs.type < rhs.type)
+ return -1;
+ return 1;
}
- using namespace der;
-
- auto const p = parsePreamble(s, ec);
- if (ec)
- return {};
-
- // All fulfillments are context-specific, constructed
- // types
- if (!isConstructed(p) || !isContextSpecific(p))
{
- ec = error::malformed_encoding;
- return {};
+ // compare lengths
+ auto const lhsL = cryptoconditions::der::withTupleEncodedLengthHelper(
+ lhs, boost::none, TagMode::automatic, traitsCache);
+ auto const rhsL = cryptoconditions::der::withTupleEncodedLengthHelper(
+ rhs, boost::none, TagMode::automatic, traitsCache);
+ if (lhsL != rhsL)
+ {
+ if (lhsL < rhsL)
+ return -1;
+ return 1;
+ }
}
- if (p.length > s.size())
{
- ec = error::buffer_underfull;
- return {};
+ // compare finger prints
+ using traits = DerCoderTraits>;
+ if (auto const r = traits::compare(lhs.fingerprint, rhs.fingerprint, traitsCache))
+ return r;
}
-
- if (s.size() > maxSerializedCondition)
+ // fingerprints were equal
{
- ec = error::large_size;
- return {};
+ // compare costs
+ using traits = DerCoderTraits>;
+ if (auto const r = traits::compare(lhs.cost, rhs.cost, traitsCache))
+ return r;
}
-
- std::unique_ptr c;
-
- switch (p.tag)
+ // costs were equal
+ auto const lhsIsCompound = Condition::isCompoundCondition(lhs.type);
+ auto const rhsIsCompound = Condition::isCompoundCondition(rhs.type);
+ if (!lhsIsCompound && !rhsIsCompound)
+ return 0;
+ if (lhsIsCompound && !rhsIsCompound)
+ return 1;
+ if (!lhsIsCompound && rhsIsCompound)
+ return -1;
+ // both are compound
+ assert(lhsIsCompound && rhsIsCompound);
{
- case 0: // PreimageSha256
- c = detail::loadSimpleSha256(
- Type::preimageSha256,
- Slice(s.data(), p.length), ec);
- if (!ec)
- s += p.length;
- break;
-
- case 1: // PrefixSha256
- ec = error::unsupported_type;
- return {};
-
- case 2: // ThresholdSha256
- ec = error::unsupported_type;
- return {};
-
- case 3: // RsaSha256
- ec = error::unsupported_type;
- return {};
-
- case 4: // Ed25519Sha256
- ec = error::unsupported_type;
- return {};
-
- default:
- ec = error::unknown_type;
- return {};
+ // compare subtypes
+ using traits = DerCoderTraits>;
+ return traits::compare(lhs.subtypes, rhs.subtypes, traitsCache);
}
+}
- if (!s.empty())
- {
- ec = error::trailing_garbage;
- return {};
- }
+} // der
- return c;
+Condition::Condition(der::Constructor const&){}
+
+std::bitset<5>
+Condition::selfAndSubtypes() const
+{
+ std::bitset<5> result{subtypes};
+ result.set(static_cast(type));
+ return result;
}
+Condition
+Condition::deserialize(Slice s, std::error_code& ec)
+{
+ // The binary encoding of conditions differs based on their
+ // type. All types define at least a fingerprint and cost
+ // sub-field. Some types, such as the compound condition
+ // types, define additional sub-fields that are required to
+ // convey essential properties of the cryptocondition (such
+ // as the sub-types used by sub-conditions in the case of
+ // the compound types).
+ //
+ // Conditions are encoded as follows:
+ //
+ // Condition ::= CHOICE {
+ // preimageSha256 [0] SimpleSha256Condition,
+ // prefixSha256 [1] CompoundSha256Condition,
+ // thresholdSha256 [2] CompoundSha256Condition,
+ // rsaSha256 [3] SimpleSha256Condition,
+ // ed25519Sha256 [4] SimpleSha256Condition
+ // }
+ //
+ // SimpleSha256Condition ::= SEQUENCE {
+ // fingerprint OCTET STRING (SIZE(32)),
+ // cost INTEGER (0..4294967295)
+ // }
+ //
+ // CompoundSha256Condition ::= SEQUENCE {
+ // fingerprint OCTET STRING (SIZE(32)),
+ // cost INTEGER (0..4294967295),
+ // subtypes ConditionTypes
+ // }
+ //
+ // ConditionTypes ::= BIT STRING {
+ // preImageSha256 (0),
+ // prefixSha256 (1),
+ // thresholdSha256 (2),
+ // rsaSha256 (3),
+ // ed25519Sha256 (4)
+ // }
+
+ using namespace der;
+
+ Condition v{der::constructor};
+
+ der::Decoder decoder(s, der::TagMode::automatic);
+ decoder >> v >> der::eos;
+
+ ec = decoder.ec_;
+ return v;
+}
}
}
diff --git a/src/ripple/conditions/impl/Der.h b/src/ripple/conditions/impl/Der.h
new file mode 100644
index 00000000000..a98848a8c91
--- /dev/null
+++ b/src/ripple/conditions/impl/Der.h
@@ -0,0 +1,27 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_DER_H
+#define RIPPLE_CONDITIONS_DER_H
+
+#include
+#include
+#include
+
+#endif
diff --git a/src/ripple/conditions/impl/DerCoder.cpp b/src/ripple/conditions/impl/DerCoder.cpp
new file mode 100644
index 00000000000..31b2e859077
--- /dev/null
+++ b/src/ripple/conditions/impl/DerCoder.cpp
@@ -0,0 +1,715 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+namespace der {
+
+Tag::Tag(SequenceTag)
+ : classId(ClassId::universal), tagNum(16), primitive(false)
+{
+}
+
+Tag::Tag(SetTag)
+ : classId(ClassId::universal), tagNum(17), primitive(false)
+{
+}
+
+bool
+Tag::isSet() const
+{
+ return classId == ClassId::universal && tagNum == 17;
+}
+
+//------------------------------------------------------------------------------
+
+std::uint64_t
+tagNumLength(std::uint64_t v)
+{
+ if (v <= 30)
+ return 1;
+
+ constexpr std::uint64_t chunkSize = 7;
+ std::uint64_t const nChunks = 1 + 8 * sizeof(v) / chunkSize;
+ auto const lz = numLeadingZeroChunks(v, nChunks);
+ assert(lz != nChunks);
+ return nChunks - lz + 1;
+}
+
+void
+encodeTagNum(MutableSlice& dst, std::uint64_t v, std::error_code& ec)
+{
+ assert(v > 30);
+
+ auto n = tagNumLength(v) - 1;
+
+ if (dst.size() != n)
+ {
+ ec = make_error_code(Error::logicError);
+ return;
+ }
+ while (n--)
+ {
+ auto b = static_cast((v >> (n * 7)) & 0xFF);
+ // all but the last byte has the high order bit set
+ if (n)
+ b |= 1 << 7;
+ else
+ b &= ~(1 << 7);
+ dst.push_back(static_cast(b));
+ }
+}
+
+void
+decodeTag(Slice& slice, Tag& tag, std::error_code& ec)
+{
+ auto popFront = [&]() -> std::uint8_t {
+ if (slice.empty())
+ {
+ ec = make_error_code(Error::shortGroup);
+ return 0;
+ }
+ auto const r = slice[0];
+ slice += 1;
+ return r;
+ };
+
+ std::uint8_t curByte = popFront();
+ if (ec)
+ return;
+
+ tag.classId = static_cast(curByte >> 6);
+ tag.primitive = !(curByte & (1 << 5));
+
+ // decode the tag
+ if ((curByte & 0x1f) != 0x1f)
+ {
+ tag.tagNum = curByte & 0x1f;
+ }
+ else
+ {
+ std::uint64_t tagNum = 0;
+ do
+ {
+ curByte = popFront();
+ if (ec)
+ return;
+ auto const asBase128 = curByte & ~(1 << 7);
+
+ if (tagNum & (static_cast(0xfe)
+ << 8 * (sizeof(tagNum) - 1)))
+ {
+ // Shifting by 7 bits would overflow tagNum
+ ec = make_error_code(Error::tagOverflow);
+ return;
+ }
+
+ tagNum = (tagNum << 7) | asBase128;
+
+ if (!tagNum)
+ {
+ // leading zeros
+ ec = make_error_code(Error::badDerEncoding);
+ return;
+ }
+
+ } while (curByte & (1 << 7));
+
+ tag.tagNum = tagNum;
+ if (tagNum <= 30)
+ {
+ // tag was encoded with the long form, but should have been short
+ // form
+ ec = make_error_code(Error::badDerEncoding);
+ return;
+ }
+ }
+}
+
+std::uint64_t
+contentLengthLength(std::uint64_t v)
+{
+ if (v <= 127)
+ return 1;
+
+ constexpr std::uint64_t chunkSize = 8;
+ constexpr std::uint64_t nChunks = sizeof(v);
+ auto const lz = numLeadingZeroChunks(v, nChunks);
+ return nChunks - lz + 1;
+}
+
+void
+encodeContentLength(MutableSlice& dst, std::uint64_t v, std::error_code& ec)
+{
+ if (v <= 127)
+ {
+ if (dst.size() != 1)
+ {
+ ec = make_error_code(Error::logicError);
+ return;
+ }
+ dst.push_back(static_cast(v));
+ return;
+ }
+
+ auto n = contentLengthLength(v);
+
+ if (dst.size() != n)
+ {
+ ec = make_error_code(Error::logicError);
+ return;
+ }
+
+ --n;
+ dst.push_back(static_cast(n) | (1 << 7));
+
+ while (n--)
+ dst.push_back(static_cast((v >> (n * 8)) & 0xFF));
+}
+
+void
+decodeContentLength(Slice& slice, std::uint64_t& contentLength, std::error_code& ec)
+{
+ auto popFront = [&]() -> std::uint8_t {
+ if (slice.empty())
+ {
+ ec = make_error_code(Error::shortGroup);
+ return 0;
+ }
+ auto const r = slice[0];
+ slice += 1;
+ return r;
+ };
+
+ contentLength = 0;
+
+ std::uint8_t curByte = popFront();
+ if (ec)
+ return;
+ if (curByte <= 127)
+ {
+ contentLength = curByte;
+ }
+ else if ((curByte & ~(1 << 7)) > 8)
+ {
+ ec = make_error_code(Error::unsupported);
+ return;
+ }
+ else
+ {
+ int const n = (curByte & ~(1 << 7));
+ for (int i = 0; i < n; ++i)
+ {
+ curByte = popFront();
+ if (ec)
+ return;
+ contentLength = (contentLength << 8) | curByte;
+ }
+ }
+}
+
+std::uint64_t
+tagLength(Tag t)
+{
+ return tagNumLength(t.tagNum);
+}
+
+void
+encodePreamble(MutableSlice& dst, Preamble const& p, std::error_code& ec)
+{
+ if (dst.size() <= 1)
+ {
+ ec = make_error_code(Error::logicError);
+ return;
+ }
+
+ char d = (static_cast(p.tag_.classId) << 6);
+ if (!p.tag_.primitive)
+ d |= 1 << 5;
+
+ if (p.tag_.tagNum <= 30)
+ {
+ d |= p.tag_.tagNum;
+ dst.push_back(d);
+ }
+ else
+ {
+ d |= 0x1f;
+ dst.push_back(d);
+ encodeTagNum(dst, p.tag_.tagNum, ec);
+ if (ec)
+ return;
+ }
+ encodeContentLength(dst, p.contentLength_, ec);
+}
+
+void
+decodePreamble(Slice& slice, Preamble& p, std::error_code& ec)
+{
+ decodeTag(slice, p.tag_, ec);
+ if (ec)
+ return;
+ decodeContentLength(slice, p.contentLength_, ec);
+}
+
+//------------------------------------------------------------------------------
+
+Group::Group(
+ Tag t,
+ TagMode tagMode,
+ GroupType groupType,
+ MutableSlice slice)
+ : id_(t)
+ , tagMode_(tagMode)
+ , groupType_(groupType)
+ , slice_(slice)
+{
+}
+
+MutableSlice&
+Group::slice()
+{
+ return slice_;
+}
+
+Slice
+Group::slice() const
+{
+ return slice_;
+}
+
+bool
+Group::isSet() const
+{
+ return id_.isSet();
+}
+
+bool
+Group::isAutoSequence() const
+{
+ return tagMode_ == TagMode::automatic &&
+ groupType_ == GroupType::autoSequence;
+}
+
+bool
+Group::isChoice() const
+{
+ return groupType_ == GroupType::choice;
+}
+
+void
+Group::set(bool primitive, GroupType bt)
+{
+ id_.primitive = primitive;
+ groupType_ = bt;
+}
+
+size_t
+Group::numChildren() const
+{
+ return numChildren_;
+}
+
+GroupType Group::groupType() const
+{
+ return groupType_;
+}
+
+Eos eos;
+Automatic automatic;
+Constructor constructor;
+
+//------------------------------------------------------------------------------
+
+Encoder::Encoder(TagMode tagMode) : tagMode_(tagMode)
+{
+}
+
+Encoder::~Encoder()
+{
+ if (ec_)
+ return;
+
+ // hitting this assert means the encoding stream was not terminated with a
+ // call to eos(); The usual way to do this is with the `eos` object:
+ // encoder << someObject << eos;
+ // Certain error checks can only happen after the stream knows there are not
+ // other objects to be encoded.
+ assert(atEos_);
+}
+
+void
+Encoder::startGroup(Tag t, GroupType groupType, std::uint64_t contentSize)
+{
+ if (ec_)
+ return;
+
+ assert(!root_);
+
+ if (groupType == GroupType::choice && parentIsChoice())
+ {
+ // Choice/choice groups are not supported
+ ec_ = make_error_code(Error::unsupported);
+ return;
+ }
+
+ if (parentIsChoice() && tagMode_ == TagMode::automatic)
+ {
+ auto g = subgroups_.top();
+ g.set(t.primitive, groupType);
+ subgroups_.emplace(std::move(g));
+ return;
+ }
+
+ auto const contentLL = contentLengthLength(contentSize);
+ auto const tagL = tagLength(t);
+ auto const sliceSize = contentSize + contentLL + tagL;
+
+ auto const parentSlice = [&]
+ {
+ if (!subgroups_.empty())
+ return subgroups_.top().slice();
+ assert(rootBuf_.empty());
+ rootBuf_.resize(sliceSize);
+ rootSlice_ = Slice{rootBuf_.data(), rootBuf_.size()};
+ return MutableSlice(rootBuf_.data(), rootBuf_.size());
+ }();
+
+ if (sliceSize > parentSlice.size())
+ {
+ // incorrect length calculation
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+
+ MutableSlice thisSlice{parentSlice.data(), sliceSize};
+
+ auto const preambleLength = sliceSize - contentSize;
+ if (preambleLength > thisSlice.size())
+ {
+ // incorrect length calculation
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+ MutableSlice preambleSlice{thisSlice.data(), preambleLength};
+ encodePreamble(preambleSlice, Preamble{t, contentSize}, ec_);
+ if (ec_)
+ return;
+ if (!preambleSlice.empty())
+ {
+ // incorrect length calculation
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+ thisSlice += preambleLength;
+
+ subgroups_.emplace(t, tagMode_, groupType, thisSlice);
+};
+
+void
+Encoder::endGroup()
+{
+ if (ec_)
+ return;
+
+ if (subgroups_.empty())
+ {
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+
+ Group top(std::move(subgroups_.top()));
+ subgroups_.pop();
+
+ if (!top.slice().empty())
+ {
+ // incorrect length calculation
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+
+ if (parentIsChoice() && tagMode_ == TagMode::automatic)
+ {
+ // copy the child group, but don't add it to the parent
+ subgroups_.top() = std::move(top);
+ return;
+ }
+
+ if (subgroups_.empty())
+ {
+ assert(!root_);
+ root_.emplace(std::move(top));
+ return;
+ }
+
+ auto& parentSlice = subgroups_.top().slice();
+ auto const inc = std::distance(parentSlice.data(), top.slice().data());
+ if (inc < 0 || inc > parentSlice.size())
+ {
+ // incorrect length calculation
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+ parentSlice += inc;
+ subgroups_.top().incrementNumChildren();
+};
+
+void
+Encoder::eos()
+{
+ atEos_ = true;
+
+ if (ec_)
+ return;
+
+ if (!subgroups_.empty())
+ {
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+}
+
+size_t
+Encoder::size() const
+{
+ return rootSlice_.size();
+}
+
+MutableSlice&
+Encoder::parentSlice()
+{
+ static MutableSlice empty{nullptr, 0};
+ if (!subgroups_.empty())
+ return subgroups_.top().slice();
+ return empty;
+}
+
+std::error_code const&
+Encoder::ec() const
+{
+ return ec_;
+}
+
+std::vector const&
+Encoder::
+serializationBuffer(std::error_code& ec) const
+{
+ if (ec_)
+ {
+ ec = ec_;
+ return rootBuf_;
+ }
+
+ if (!root_ || rootSlice_.size() != rootBuf_.size())
+ ec = make_error_code(Error::logicError);
+
+ return rootBuf_;
+}
+
+bool
+Encoder::parentIsAutoSequence() const
+{
+ return tagMode_ == TagMode::automatic && !subgroups_.empty() &&
+ subgroups_.top().isAutoSequence();
+}
+
+bool
+Encoder::parentIsChoice() const
+{
+ return !subgroups_.empty() && subgroups_.top().isChoice();
+}
+
+//------------------------------------------------------------------------------
+
+Decoder::Decoder(Slice slice, TagMode tagMode)
+ : tagMode_(tagMode), rootSlice_(slice)
+{
+}
+
+Decoder::~Decoder()
+{
+ if (ec_)
+ return;
+
+ // hitting this assert means the decoding stream was not terminated with a
+ // call to eos(); The usual way to do this is with the `eos` object:
+ // decoder >> someObject >> eos;
+ // Certain error checks can only happen after the stream knows there are not
+ // other objects to be encoded.
+ assert(atEos_);
+}
+
+void
+Decoder::startGroup(boost::optional const& t, GroupType groupType)
+{
+ if (ec_)
+ return;
+
+ if (groupType == GroupType::choice && parentIsChoice())
+ {
+ // Choice/choice groups are not supported
+ ec_ = make_error_code(Error::unsupported);
+ return;
+ }
+
+ if (parentIsChoice() && tagMode_ == TagMode::automatic)
+ {
+ if (std::get(ancestors_.top()) > 0)
+ {
+ // choice groups must have exactly one child, and adding this child
+ // would violate that constraint
+ ec_ = make_error_code(Error::badDerEncoding);
+ return;
+ }
+ auto a = ancestors_.top();
+ std::get(a) = groupType;
+ ancestors_.emplace(a);
+ return;
+ }
+
+ Preamble p;
+ (*this) >> p;
+
+ if (!(groupType == GroupType::choice && tagMode_ == TagMode::automatic))
+ {
+ if (t && (p.tag_ != t))
+ {
+ ec_ = make_error_code(Error::preambleMismatch);
+ return;
+ }
+ }
+
+ auto const& s = parentSlice();
+ if (p.contentLength_ > s.size())
+ {
+ ec_ = make_error_code(Error::shortGroup);
+ return;
+ }
+ ancestors_.emplace(Slice{s.data(), p.contentLength_}, p.tag_, groupType, 0);
+}
+
+void
+Decoder::endGroup()
+{
+ if (ec_)
+ return;
+
+ if (ancestors_.empty())
+ {
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+
+ if (std::get(ancestors_.top()) == GroupType::choice &&
+ tagMode_ == TagMode::automatic &&
+ std::get(ancestors_.top()) != 1)
+ {
+ // choice groups must have exactly one child
+ ec_ = make_error_code(Error::badDerEncoding);
+ return;
+ }
+
+ auto const poped = std::get(ancestors_.top());
+ ancestors_.pop();
+ if (!poped.empty())
+ {
+ ec_ = make_error_code(Error::longGroup);
+ return;
+ }
+
+ if (!ancestors_.empty() &&
+ std::get(ancestors_.top()) == GroupType::choice &&
+ tagMode_ == TagMode::automatic)
+ {
+ // track children to make sure choices always have exactly one child
+ ++std::get(ancestors_.top());
+ }
+
+ auto& parent = parentSlice();
+ auto const toConsume = std::distance(parent.data(), poped.data());
+ if (toConsume < 0 || toConsume > parent.size())
+ {
+ // incorrect length calculation
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+ parent += toConsume;
+}
+
+void
+Decoder::eos()
+{
+ atEos_ = true;
+ if (ec_)
+ return;
+
+ if (!ancestors_.empty())
+ {
+ ec_ = make_error_code(Error::logicError);
+ return;
+ }
+ if (!rootSlice_.empty())
+ {
+ ec_ = make_error_code(Error::longGroup);
+ return;
+ }
+}
+
+boost::optional
+Decoder::parentTag() const
+{
+ if (ancestors_.empty())
+ return boost::none;
+ return std::get(ancestors_.top());
+}
+
+Slice&
+Decoder::parentSlice()
+{
+ if (!ancestors_.empty())
+ return std::get(ancestors_.top());
+ return rootSlice_;
+};
+
+bool
+Decoder::parentIsAutoSequence() const
+{
+ return tagMode_ == TagMode::automatic && !ancestors_.empty() &&
+ std::get(ancestors_.top()) == GroupType::autoSequence;
+}
+
+bool
+Decoder::parentIsChoice() const
+{
+ return !ancestors_.empty() &&
+ std::get(ancestors_.top()) == GroupType::choice;
+}
+
+std::error_code const&
+Decoder::ec() const
+{
+ return ec_;
+}
+
+} // der
+} // ripple
+} // cryptoconditions
diff --git a/src/ripple/conditions/impl/DerCoder.h b/src/ripple/conditions/impl/DerCoder.h
new file mode 100644
index 00000000000..a5eb3a47f33
--- /dev/null
+++ b/src/ripple/conditions/impl/DerCoder.h
@@ -0,0 +1,1000 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_DERCODER_H
+#define RIPPLE_CONDITIONS_DERCODER_H
+
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+
+/**
+ The `der` namespace contains a set of classes the implement ASN.1 DER encoding
+ and decoding for cryptoconditions.
+
+ There are two keys to understanding how to use these coders: 1)
+ DerCoderTraits, and 2) `withTuple`.
+
+ To encode or decode a type `T`, a specialization of DerCoderTraits must
+ exist. This specialization contains all the functions specific to streaming
+ type `T`. The most important are: `encode`, `decode`, `length`, and `compare`.
+ @see {@link #DerCoderTraits}.
+
+ If a class defines the function `withTuple`, that class can use the helper
+ functions `withTupleEncodeHelper`, `withTupleDecodeHelper`,
+ `withTupleEncodedLengthHelper`, and `withTupleCompareHelper`. The `withTuple`
+ function takes a callable, and the class will wrap its member variables in a
+ tuple of references (usually using `tie`), and call the callable with the
+ tuple as an argument. See one of the existing cryptocondtion implementations
+ for an example of how `withTuple` is used.
+
+ @note Efficiently encoding cryptocondition classes into ASN.1 has some challenges:
+
+ 1) The size of the preamble depends on the size of content being encoded. This
+ makes it difficult to encode in a single pass. The most natural implementation
+ would a) encode the content, b) encode the preamble, c) copy the result into
+ some final buffer. The function `DerCoderTraits::length` solves this
+ problem. When encoding a value of type T, the length will return the number of
+ bytes used to encode contents of the value (but does not include the
+ preamble).
+
+ 2) Encoding DER sets requires the elements of the set be encoded in sorted
+ order (sorted by the encoding of the individual elements). The function
+ `DerCoderTraits::compare` solves this problem. This function returns a
+ values less than 0 if the lhs < rhs, 0 if lhs == rhs, and a value greater than
+ 0 if lhs > rhs.
+
+ 3) When encoding cryptoconditions that contain other cryptoconditions in
+ hierarchies (such as threshold and prefix), some values - like length and sort
+ order - will be computed multiple times, and the number of times a value is
+ computed grows as a function of the cryptocondition's depth in the hierarchy.
+ This is solved with a TraitCache. This caches previously computed values of
+ length and sort order so they do not need to be recomputed. Note that storing
+ values in the cache is type dependent, and the address of the variable must be
+ stable while encoding. It makes sense to cache higher level values, but not
+ primitives.
+ */
+
+namespace der {
+
+struct Encoder;
+struct Decoder;
+
+/// constructor tag to specify an ASN.1 sequence
+struct SequenceTag {};
+/// constructor tag to specify an ASN.1 set
+struct SetTag {};
+
+/// the type information part of an ASN.1 preamble
+struct Tag
+{
+ ClassId classId = ClassId::universal;
+ std::uint64_t tagNum = 0;
+ bool primitive = true;
+
+ Tag() = default;
+
+ Tag(ClassId classId_, std::uint64_t tagNum_, bool primitive_)
+ : classId(classId_), tagNum(tagNum_), primitive(primitive_)
+ {
+ }
+
+ template
+ Tag(DerCoderTraits t, std::uint64_t tn)
+ : Tag(DerCoderTraits::classId(), tn, DerCoderTraits::primitive())
+ {
+ }
+
+ explicit
+ Tag(SequenceTag);
+
+ explicit
+ Tag(SetTag);
+
+ /// return true if the tag represents an ASN.1 set
+ bool
+ isSet() const;
+
+ friend bool
+ operator<(Tag const& lhs, Tag const& rhs)
+ {
+ return std::tie(lhs.classId, lhs.tagNum, lhs.primitive) <
+ std::tie(rhs.classId, rhs.tagNum, rhs.primitive);
+ }
+ friend bool
+ operator==(Tag const& lhs, Tag const& rhs)
+ {
+ return lhs.classId == rhs.classId && lhs.tagNum == rhs.tagNum &&
+ lhs.primitive == rhs.primitive;
+ }
+ friend bool
+ operator!=(Tag const& lhs, Tag const& rhs)
+ {
+ return !operator==(lhs, rhs);
+ }
+};
+
+/** an ans.1 preamble
+
+ values are encoded in ans.1 with a preamble that specifies how to interpret
+ the content, followed by the content. This struct represents the preamble.
+*/
+struct Preamble
+{
+ /// type information
+ Tag tag_;
+ /// content length in bytes
+ std::uint64_t contentLength_;
+};
+
+/** RAII class for coder groups
+
+ ASN.1 values are coded as a hierarchy. There are root values, which have
+ sub-values as children. A `GroupGuard` organizes the serialization code so
+ C++ scopes represent levels in the ASN.1 hierarchy. The constructor pushes a
+ new group onto the coders group stack, and the destructor pops the group.
+ Entering a scope represents a new value that will be coded. New values will
+ be descendants of this value coded in this scope until the scope is exited.
+ */
+template
+class GroupGuard
+{
+ /// The encoder or decoder
+ Coder& s_;
+
+public:
+ GroupGuard(Coder& s, Tag t, GroupType bt)
+ : s_(s)
+ {
+ s_.startGroup(t, bt);
+ }
+
+ GroupGuard(Coder& s, Tag t, GroupType bt, std::uint64_t contentSize)
+ : s_(s)
+ {
+ s_.startGroup(t, bt, contentSize);
+ }
+
+ GroupGuard(Coder& s, boost::optional const& t, GroupType bt)
+ : s_(s)
+ {
+ s_.startGroup(t, bt);
+ }
+
+ GroupGuard(Coder& s, SequenceTag t)
+ : GroupGuard(s, Tag{t}, GroupType::sequence)
+ {
+ }
+
+ GroupGuard(Coder& s, SetTag t)
+ : GroupGuard(s, Tag{t}, GroupType::set)
+ {
+ }
+
+ template
+ GroupGuard(Coder& s, DerCoderTraits t)
+ : s_(s)
+ {
+ boost::optional tag;
+ if (auto const tagNum = t.tagNum())
+ tag.emplace(t, *tagNum);
+ s_.startGroup(tag, t.groupType());
+ }
+
+ template
+ GroupGuard(Coder& s, T const& v, DerCoderTraits t)
+ : s_(s)
+ {
+ auto const tagNum = t.tagNum(v);
+ Tag tag(t, tagNum);
+ s_.startGroup(tag, t.groupType());
+ }
+
+ // Needed for fuzz testing
+ GroupGuard(Coder& s, GroupType bt)
+ : s_(s)
+ {
+ s_.startGroup(boost::none, bt);
+ }
+
+ ~GroupGuard()
+ {
+ s_.endGroup();
+ }
+};
+
+/** End of stream guard
+
+ Coders need to know when when a serialization is complete. Clients signal
+ this by calling `eos`. This guard calls `eos` in the destructor so leaving
+ a scope may be used to signal `eos`.
+
+ @note: This class is mostly used for testing. The usual way to signal `eos`
+ is by adding `der::eos` at the end of a stream. For example: `coder << value
+ << der::eos;`
+ */
+template
+class EosGuard
+{
+ // Encoder or decoder
+ Coder& s_;
+
+public:
+ explicit
+ EosGuard(Coder& s)
+ : s_(s)
+ {
+ }
+
+ ~EosGuard()
+ {
+ s_.eos();
+ }
+};
+
+template
+std::uint64_t
+numLeadingZeroChunks(std::uint64_t v, std::uint64_t n)
+{
+ static_assert(ChunkBitSize <= 8, "Unsupported chunk bit size");
+
+ std::uint64_t result = 0;
+ while (n--)
+ {
+ auto b = static_cast((v >> (n * ChunkBitSize)) & 0xFF);
+ if (b)
+ break;
+ ++result;
+ }
+ return result;
+}
+
+/** decode the tag from ASN.1 format
+ */
+void
+decodeTag(Slice& slice, Tag& tag, std::error_code& ec);
+
+/** Encode the integer in a format appropriate for an ans.1 tag number.
+
+ Encode the integer in big endian form, in as few of bytes as possible. All
+ but the last byte has the high order bit set. The number is encoded in base
+ 128 (7-bits each).
+*/
+void
+encodeTagNum(MutableSlice& dst, std::uint64_t v, std::error_code& ec);
+
+/** Return the number of bytes required to encode a tag with the given tag num */
+std::uint64_t
+tagNumLength(std::uint64_t v);
+
+/** Decode the content length from ASN.1 format
+*/
+void
+decodeContentLength(Slice& slice, std::uint64_t& contentLength, std::error_code& ec);
+
+/** Encode the integer in a format appropriate for an ans.1 content length
+
+ Encode the integer in big endian form, in as few of bytes as possible.
+*/
+void
+encodeContentLength(MutableSlice& dst, std::uint64_t v, std::error_code& ec);
+
+/** return the number of bytes required to encode the given content length
+ */
+std::uint64_t
+contentLengthLength(std::uint64_t);
+
+/** return the number of bytes required to encode the given tag
+ */
+std::uint64_t
+tagLength(Tag t);
+
+/** return the number of bytes required to encode the value, including the preamble
+ */
+template
+std::uint64_t
+totalLength(
+ T const& v,
+ boost::optional const& parentGroupType,
+ TagMode encoderTagMode,
+ TraitsCache& traitsCache,
+ boost::optional const& childNumber)
+{
+ auto const contentLength =
+ Trait::length(v, parentGroupType, encoderTagMode, traitsCache);
+ if (encoderTagMode == TagMode::automatic &&
+ parentGroupType && *parentGroupType == GroupType::choice)
+ return contentLength;
+
+ auto const oneTagResult = tagNumLength(childNumber.value_or(0)) +
+ contentLength + contentLengthLength(contentLength);
+
+ if (parentGroupType && *parentGroupType == GroupType::autoSequence &&
+ DerCoderTraits::groupType() == GroupType::choice)
+ {
+ // auto sequences with a choice write a two tags: one for the sequence
+ // number and one for the choice
+ // note: This breaks down if the choice number is large enough to
+ // require more than one byte for the tag (more than 30 choices)
+ return tagNumLength(0) + oneTagResult + contentLengthLength(oneTagResult);
+ }
+
+ // all cryptocondition preambles are one byte
+ return oneTagResult;
+}
+
+/** A value in a hierarchy of values when encoding
+
+ ASN.1 values are coded as a hierarchy. There is one root value, which has
+ sub-values as children. When encoding, this class keeps track the type that
+ is being encoded, what bytes in the stream represent content for this value,
+ and child values.
+
+ @note: decoders use a different class to represent the hierarchy of values.
+ */
+class Group
+{
+ /// ASN.1 type information for the value being encoded
+ Tag id_;
+ /// current number of children
+ size_t numChildren_ = 0;
+ /// ASN.1 explicit (direct) or automatic tagging
+ TagMode tagMode_;
+ /// additional type information for the group
+ GroupType groupType_;
+
+ /** data slice reserved for both the preamble and contents of the group
+
+ @note: it _must_ be the correct size. It will not be resized.
+ */
+ MutableSlice slice_;
+
+public:
+ Group(Group const&) = default;
+ Group(Group&&) = default;
+ Group&
+ operator=(Group&&) = default;
+ Group&
+ operator=(Group const&) = default;
+
+ Group(
+ Tag t,
+ TagMode tagMode,
+ GroupType groupType,
+ MutableSlice slice);
+
+ /// the data slice reserved for both the preamble and contents of the group
+ MutableSlice&
+ slice();
+
+ Slice
+ slice() const;
+
+ /** Increment the number of children this group has
+ */
+ void
+ incrementNumChildren()
+ {
+ ++numChildren_;
+ }
+
+ /// return true if the group represents an ASN.1 set
+ bool
+ isSet() const;
+
+ /** return true if the group represents an auto sequence
+
+ @note an auto sequence is an ASN.1 sequence that has autogenerated
+ tag numbers
+ */
+ bool
+ isAutoSequence() const;
+
+ /// return true if the group represents an ASN.1 choice
+ bool
+ isChoice() const;
+
+ /** set the groups type information
+
+ @param primitive true is primitive, false if constructed
+ @param bt the groups type information
+ */
+ void
+ set(bool primitive, GroupType bt);
+
+ /// return the number of sub-values
+ size_t
+ numChildren() const;
+
+ GroupType groupType() const;
+};
+
+/** encode the preamble into the dst slice
+ */
+void
+encodePreamble(MutableSlice& dst, Preamble const& p, std::error_code& ec);
+
+/** decode the preamble from slice into p
+ */
+void
+decodePreamble(Slice& slice, Preamble& p, std::error_code& ec);
+
+/** type representing and end of stream
+
+ Coders need to know when when a serialization is complete. Clients signal
+ this by calling `eos`. The typical way of calling `eos` is by serializing a
+ value of type Eos. There is a convenience global variable for this purpose.
+ It will typically be used as follows: `coder << value << der::eos;`
+*/
+struct Eos {};
+extern Eos eos;
+/// constructor tag to specify a decoder in automatic mode
+struct Automatic {};
+extern Automatic automatic;
+/** constructor tag to specify a type is being constructed for decoding into
+
+ Often, it is convenient to create a type and then decode into that type.
+ However, this would usually require that type to be default constructable
+ (as the contents used to create are deserialized after the variable is
+ constructed). This `Constructor` type is used to create constructors and
+ specify that they should only be used for DER decoding.
+ */
+struct Constructor {};
+extern Constructor constructor;
+
+
+/** Stream interface to encode values into ASN.1 DER format
+
+ The encoder class has an interface similar to a c++ output stream. Values
+ are added to the stream using the `<<` operator. After all the values are
+ added to the encoder, it must be terminated with a call to `eos()`. As a
+ convenience, there is a special variable called `eos` that when streamed will
+ call the stream's `eos()` function. Typically, the code to encode values to a
+ stream is: `encoder << value_1 << ... << value_n << eos;`.
+
+ Every type to be streamed must specialize the DerCoderTraits class @see
+ {@link #DerCoderTraits}. There exist specializations for some C++ types and
+ primitive rippled types - including integers, strings, bitstrings, tuples,
+ buffers, arrays, and wrappers for wrapping collections like vector into
+ either ASN.1 sets or sequences.
+
+ After the values are written, the stream should be checked for errors. The
+ function `ec` will return the error code of the first error encountered
+ while streaming. Streaming will stop after the first error.
+
+ Once the values are streamed, the actual encoding is retrieved by calling
+ the `write` function.
+
+ Encoding and decoding values often have the same code structure. The only
+ difference is encoding will use `operator<<` and decoding will use
+ `operator>>`. To allow writing generic code, both encoders and decoders
+ support `operator&`. Typically, the generic code both encode and decode is:
+ `coder & value_1 & ... & value_n & eos;`
+ */
+struct Encoder
+{
+ /// explicit or automatic tagging
+ TagMode tagMode_ = TagMode::direct;
+
+ /** values are coded as a hierarchy. `subgroups_` tracks the current
+ position in the hierarchy.
+
+ The bottom of the stack is the root value, the top of the stack is the
+ current parent.
+ */
+ std::stack subgroups_;
+
+ /** root of the tree of groups that were encoded
+
+ @note: This is not populated until after encoding is complete
+ */
+ boost::optional root_;
+
+ /** Buffer to encode into */
+ std::vector rootBuf_;
+
+ /** Slice to encode into
+
+ @note: rootBuf_ should contain the same information as rootSlice_, and
+ `rootSlice_` may be removed in the future. It is kept as a debugging
+ tool to make sure rootBuf_ is not resized after it is resized for the
+ root group.
+ */
+ Slice rootSlice_;
+
+ /** the error code of the first error encountered
+
+ @note after the error code is set encoding stops
+ */
+ std::error_code ec_;
+
+ /** true if the `eos` function has been called
+
+ some error handling cannot happen until all the values have been coded.
+ `atEos_` ensures every stream is terminated with an `eos` call so those
+ error checks can be run.
+ */
+ bool atEos_ = false;
+
+ /** traitsCache cache some values that need to be repeatedly computed and may be expensive to compute.
+
+ Some value types will cache lengths and sort orders, other values types will not cache any values.
+ */
+ TraitsCache traitsCache_;
+
+ explicit
+ Encoder(TagMode tagMode);
+ ~Encoder();
+
+ /// prepare to add a new value as a child of the current value
+ void
+ startGroup(Tag t, GroupType groupType, std::uint64_t contentSize);
+
+ /// finish adding the new value
+ void
+ endGroup();
+
+ /** terminate the stream
+
+ Streams must be terminated before the destructor is called. Certain error checks
+ cannot occur until the encoder knows streaming is complete. Calling `eos()` runs these
+ error checks. Failing to call `eos` before the destructor is an error.
+ */
+ void
+ eos();
+
+ /// total size in bytes of the content and all the preambles
+ size_t
+ size() const;
+
+ /// return the portion of the buffer that represents the parent value
+ MutableSlice&
+ parentSlice();
+
+ /** return the first error code encountered
+
+ ec should be checked after streaming to ensure no errors occurred
+ */
+ std::error_code const&
+ ec() const;
+
+ /** get the serialization buffer that contains the values encoded as ASN.1 der
+ */
+ std::vector const&
+ serializationBuffer(std::error_code& ec) const;
+
+ /** return true if the group at the top of the stack represents an auto
+ sequence
+
+ @note an auto sequence is an ASN.1 sequence that has autogenerated
+ tag numbers
+ */
+ bool
+ parentIsAutoSequence() const;
+
+ /** return true if the group at the top of the stack represents an ASN.1
+ choice
+ */
+ bool
+ parentIsChoice() const;
+
+ /** Add values to the encoder
+ @{
+ */
+ friend
+ Encoder&
+ operator&(Encoder& s, Eos e)
+ {
+ s.eos();
+ return s;
+ }
+
+ template
+ friend
+ Encoder&
+ operator&(Encoder& s, T const& v)
+ {
+ if (s.ec_)
+ return s;
+
+ using traits = DerCoderTraits>;
+ auto const groupType = traits::groupType();
+
+ auto contentSize = [&] {
+ boost::optional parentGroupType;
+ if (!s.subgroups_.empty())
+ parentGroupType.emplace(s.subgroups_.top().groupType());
+ return traits::length(v, parentGroupType, s.tagMode_, s.traitsCache_);
+ };
+
+ if (s.parentIsAutoSequence())
+ {
+ if (groupType == GroupType::choice)
+ {
+ Tag const tag1{ClassId::contextSpecific,
+ s.subgroups_.top().numChildren(),
+ traits::primitive()};
+ Tag const tag2{traits{}, traits::tagNum(v)};
+ auto const contentSize = traits::length(
+ v, GroupType::sequenceChild, s.tagMode_, s.traitsCache_);
+ GroupGuard g1(s, tag1, GroupType::sequenceChild,
+ tagLength(tag2) + contentLengthLength(contentSize) + contentSize);
+ if (s.ec_)
+ return s;
+ GroupGuard g2(s, tag2, groupType, contentSize);
+ if (s.ec_)
+ return s;
+ traits::encode(s, v);
+ }
+ else
+ {
+ Tag const tag{ClassId::contextSpecific,
+ s.subgroups_.top().numChildren(),
+ traits::primitive()};
+ GroupGuard g(s, tag, groupType, contentSize());
+ if (s.ec_)
+ return s;
+ traits::encode(s, v);
+ }
+ }
+ else
+ {
+ Tag const tag{traits{}, traits::tagNum(v)};
+ GroupGuard g(s, tag, groupType, contentSize());
+ if (s.ec_)
+ return s;
+ traits::encode(s, v);
+ }
+
+ return s;
+ }
+
+ template
+ friend Encoder&
+ operator<<(Encoder& s, T const& t)
+ {
+ return s & t;
+ }
+ /** @} */
+};
+
+//------------------------------------------------------------------------------
+
+/** Stream interface to decode values from ASN.1 DER format
+
+ The decode class has an interface similar to a c++ output stream. Values are
+ decoded from the stream using the `>>` operator. After all the values are
+ decoded, it must be terminated with a call to `eos()`. As a convenience, there
+ is a special variable called `eos` that when streamed will call the stream's
+ `eos()` function. Typically, the code to encode values to a stream is:
+ `decoder >> value_1 >> ... >> value_n >> eos;`.
+
+ Every type to be streamed must specialize the DerCoderTraits class @see
+ {@link #DerCoderTraits}. There exist specializations for some C++ types and
+ primitive rippled types - including integers, strings, bitstrings, tuples,
+ buffers, arrays, and wrappers for wrapping collections like vector into
+ either ASN.1 sets or sequences.
+
+ After the values are decoded, the stream should be checked for errors. The
+ function `ec` will return the error code of the first error encountered
+ while decoding. Decoding will stop after the first error.
+
+ Encoding and decoding values often have the same code structure. The only
+ difference is encoding will use `operator<<` and decoding will use
+ `operator>>`. To allow writing generic code, both encoders and decoders
+ support `operator&`. Typically, the generic code both encode and decode is:
+ `coder & value_1 & ... & value_n & eos;`
+ */
+struct Decoder
+{
+ /** explicit or automatic tagging
+
+ @note this must match the mode the values were encoded with
+ */
+ TagMode tagMode_;
+
+ /** true if the `eos` function has been called
+
+ some error handling cannot happen until all the values have been coded.
+ `atEos_` ensures every stream is terminated with an `eos` call so those
+ error checks can be run.
+ */
+ bool atEos_ = false;
+
+ /** slice for the entire buffer to be decoded */
+ Slice rootSlice_;
+
+ /** values are coded as a hierarchy. `ancestors_` tracks the current
+ position in the hierarchy.
+
+ The bottom of the stack is the root value, the top of the stack is the
+ current parent.
+
+ The tuple contains the slice, ancestor tag, groupType, and number of children
+ */
+ std::stack> ancestors_;
+
+ /** the error code of the first error encountered
+
+ @note after the error code is set decoding stops
+ */
+ std::error_code ec_;
+
+ Decoder() = delete;
+
+ Decoder(Slice slice, TagMode tagMode);
+
+ ~Decoder();
+
+ /// prepare to decode a value as a child of the current value
+ void
+ startGroup(boost::optional const& t, GroupType groupType);
+
+ /** finish decoding the new value */
+ void
+ endGroup();
+
+ /** terminate the stream
+
+ Streams must be terminated before the destructor is called. Certain error checks
+ cannot occur until the encoder knows streaming is complete. Calling `eos()` runs these
+ error checks. Failing to call `eos` before the destructor is an error.
+ */
+ void
+ eos();
+
+ /** return the first error code encountered
+
+ ec should be checked after streaming to ensure no errors occurred
+ */
+ std::error_code const&
+ ec() const;
+
+ /** return the tag at the top of the ancestors stack
+
+ return boost::none if the stack is empty
+ */
+ boost::optional
+ parentTag() const;
+
+ /** return true if the ancestor at the top of the stack represents an auto
+ sequence
+
+ @note an auto sequence is an ASN.1 sequence that has autogenerated
+ tag numbers
+ */
+ bool
+ parentIsAutoSequence() const;
+
+ /** return true if the ancestor at the top of the stack represents an ASN.1
+ choice
+ */
+ bool
+ parentIsChoice() const;
+
+ /** return the portion of the buffer that represents the parent value */
+ Slice&
+ parentSlice();
+
+ /** Decode values from the encoder into variables
+
+ @note The forwarding ref is used for some value types to support
+ std::tie, SetWrapper, and SequenceWrapper (i.e. `s >> make_set(some_vec)`)
+
+ @{
+ */
+ friend
+ Decoder&
+ operator&(Decoder& s, Eos e)
+ {
+ s.eos();
+ return s;
+ }
+
+ friend
+ Decoder&
+ operator&(Decoder& s, Preamble& p)
+ {
+ if (s.ec_)
+ return s;
+
+ decodePreamble(s.parentSlice(), p, s.ec_);
+ return s;
+ }
+
+ template
+ friend
+ Decoder&
+ operator&(Decoder& s, T&& v)
+ {
+ if (s.ec_)
+ return s;
+
+ using traits = DerCoderTraits>;
+ auto const groupType = traits::groupType();
+ if (s.parentIsAutoSequence())
+ {
+ if (groupType == GroupType::choice)
+ {
+ auto& numChildren = std::get(s.ancestors_.top());
+ Tag const tag1{
+ ClassId::contextSpecific, numChildren++, traits::primitive()};
+ GroupGuard g1(s, tag1, GroupType::sequenceChild);
+ if (s.ec_)
+ return s;
+ boost::optional tag2;
+ if (auto const tagNum = traits::tagNum())
+ tag2.emplace(traits{}, *tagNum);
+ GroupGuard g2(s, tag2, groupType);
+ if (s.ec_)
+ return s;
+ traits::decode(s, v);
+ }
+ else
+ {
+ auto& numChildren = std::get(s.ancestors_.top());
+ Tag const tag{
+ ClassId::contextSpecific, numChildren++, traits::primitive()};
+ GroupGuard g(s, tag, groupType);
+ if (s.ec_)
+ return s;
+ traits::decode(s, v);
+ }
+ }
+ else
+ {
+ boost::optional tag;
+ if (auto const tagNum = traits::tagNum())
+ tag.emplace(traits{}, *tagNum);
+ GroupGuard g(s, tag, groupType);
+ if (s.ec_)
+ return s;
+ traits::decode(s, v);
+ }
+
+ return s;
+ }
+
+ template
+ friend Decoder&
+ operator>>(Decoder& s, T&& t)
+ {
+ return s & std::forward(t);
+ }
+ /** @} */
+};
+
+//------------------------------------------------------------------------------
+
+/** For types that define `withTuple`, encode the type.
+
+ @note If the user defined type defines the function `withTuple`, then
+ `withTupleEncodeHelper`, `withTupleDecodeHelper`, and
+ `withTupleEncodeHelper` may be used to help implement the DerCoderTraits
+ functions `encode`, `decode`, and `length`. The `withTuple` function should
+ take a single parameter: a callback function. That callback function should
+ take a single parameter, a tuple of references that represent the object being
+ coded.
+ */
+template
+void
+withTupleEncodeHelper(TChoice const& c, cryptoconditions::der::Encoder& encoder)
+{
+ c.withTuple(
+ [&encoder](auto const& tup) { encoder << tup; },
+ encoder.traitsCache_);
+}
+
+/** For types that define `withTuple`, decode the type.
+
+ @see note on {@link #withTupleEncodeHelper}
+ */
+template
+void
+withTupleDecodeHelper(TChoice& c, cryptoconditions::der::Decoder& decoder)
+{
+ TraitsCache dummy; // traits cache is not used in decoding
+ c.withTuple([&decoder](auto&& tup) { decoder >> tup; },
+ dummy);
+}
+
+/** For types that define `withTuple`, find the length, in bytes, of the encoded content.
+
+ @see note on {@link #withTupleEncodeHelper}
+ */
+template
+std::uint64_t
+withTupleEncodedLengthHelper(
+ TChoice const& c,
+ boost::optional const& parentGroupType,
+ TagMode encoderTagMode,
+ TraitsCache& traitsCache)
+{
+ std::uint64_t result = 0;
+ boost::optional thisGroupType(GroupType::sequence);
+ c.withTuple(
+ [&](auto const& tup) {
+ using T = std::decay_t;
+ using Traits = cryptoconditions::der::DerCoderTraits;
+ result =
+ Traits::length(tup, thisGroupType, encoderTagMode, traitsCache);
+ },
+ traitsCache);
+ return result;
+}
+
+/** For types that define `withTuple`, compare the type.
+
+ @see note on {@link #withTupleEncodeHelper}
+ */
+template
+int
+withTupleCompareHelper(
+ TChoiceDerived const& lhs,
+ TChoiceBase const& rhs,
+ TraitsCache& traitsCache)
+{
+ auto const lhsType = lhs.type();
+ auto const rhsType = rhs.type();
+ if (lhsType != rhsType)
+ {
+ if (lhsType < rhsType)
+ return -1;
+ return 1;
+ }
+
+ auto const pRhs = dynamic_cast(&rhs);
+ if (!pRhs)
+ {
+ assert(0);
+ return -1;
+ }
+
+ int result = 0;
+ lhs.withTuple(
+ [&](auto const& lhsTup) {
+ pRhs->withTuple(
+ [&](auto const& rhsTup) {
+ using traits =
+ DerCoderTraits>;
+ result = traits::compare(lhsTup, rhsTup, traitsCache);
+ },
+ traitsCache);
+ },
+ traitsCache);
+ return result;
+}
+
+
+} // der
+} // cryptoconditions
+} // ripple
+
+#endif
diff --git a/src/ripple/conditions/impl/DerPrimitiveTraits.h b/src/ripple/conditions/impl/DerPrimitiveTraits.h
new file mode 100644
index 00000000000..442c3cdc835
--- /dev/null
+++ b/src/ripple/conditions/impl/DerPrimitiveTraits.h
@@ -0,0 +1,1610 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2017 Ripple Labs Inc.
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+//==============================================================================
+
+#ifndef RIPPLE_CONDITIONS_DERPRIMITIVETRAITS_H
+#define RIPPLE_CONDITIONS_DERPRIMITIVETRAITS_H
+
+#include
+#include
+#include
+#include // must come before encoder and decoder
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace ripple {
+namespace cryptoconditions {
+namespace der {
+
+/** base class for DerCoderTraits for integer types
+
+ @see {@link #DerCoderTraits}
+*/
+struct IntegerTraits
+{
+ constexpr static
+ ClassId
+ classId()
+ {
+ return ClassId::universal;
+ }
+
+ constexpr static
+ GroupType
+ groupType()
+ {
+ return GroupType::integer;
+ }
+
+ static
+ boost::optional const&
+ tagNum()
+ {
+ static boost::optional tn{tagInteger};
+ return tn;
+ }
+
+ template
+ static
+ std::uint8_t
+ tagNum(T)
+ {
+ return tagInteger;
+ }
+
+ constexpr static
+ bool
+ primitive()
+ {
+ return true;
+ }
+
+ template
+ static
+ std::uint64_t
+ length(T const& v)
+ {
+ const auto isSigned = std::numeric_limits::is_signed;
+ if (!v || (isSigned && v == -1))
+ return 1;
+
+ std::uint64_t n = sizeof(v);
+ signed char toSkip = (isSigned && v < 0) ? 0xff : 0;
+ // skip leading 0xff for negative signed, otherwise skip leading zeros
+ // when skipping 0xff, the next octet's high bit must be set
+ // when skipping 0, the first octet's high bit must not be set
+ while (n--)
+ {
+ auto const c = static_cast((v >> (n * 8)) & 0xff);
+ if (c == toSkip &&
+ !(isSigned && v < 0 && n &&
+ (static_cast((v >> ((n - 1) * 8)) & 0xff) >= 0)))
+ continue;
+
+ if (v > 0 && c < 0)
+ return n + 2;
+ else
+ return n + 1;
+ }
+ assert(0); // will never happen
+ return 1;
+ }
+
+ template
+ static
+ std::uint64_t
+ length(
+ T const& v,
+ boost::optional const& parentGroupType,
+ TagMode encoderTagMode,
+ TraitsCache& traitsCache)
+ {
+ return length(v);
+ }
+
+ template
+ static
+ void
+ encode(Encoder& s, T v)
+ {
+ if (s.subgroups_.empty())
+ {
+ s.ec_ = make_error_code(Error::logicError);
+ return;
+ }
+
+ auto& parentSlice = s.parentSlice();
+
+ if (!v)
+ {
+ if (parentSlice.empty())
+ {
+ s.ec_ = make_error_code(Error::logicError);
+ return;
+ }
+ parentSlice.push_back(0);
+ return;
+ }
+
+ boost::optional