diff --git a/scripts/bootloader/do_sign.py b/scripts/bootloader/do_sign.py old mode 100644 new mode 100755 index 842c7e6443c1..cf613f8bde9c --- a/scripts/bootloader/do_sign.py +++ b/scripts/bootloader/do_sign.py @@ -3,19 +3,21 @@ # Copyright (c) 2018 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause - - -import sys import argparse import hashlib -from ecdsa import SigningKey +import sys +from typing import BinaryIO +from cryptography.hazmat.primitives.serialization import load_pem_private_key +from ecdsa.keys import SigningKey # type: ignore[import-untyped] -def parse_args(): + +def parse_args(argv=None): parser = argparse.ArgumentParser( description='Sign data from stdin or file.', formatter_class=argparse.RawDescriptionHelpFormatter, - allow_abbrev=False) + allow_abbrev=False + ) parser.add_argument('-k', '--private-key', required=True, type=argparse.FileType('rb'), help='Private key to use.') @@ -25,15 +27,40 @@ def parse_args(): parser.add_argument('-o', '--out', '-out', required=False, dest='outfile', type=argparse.FileType('wb'), default=sys.stdout.buffer, help='Write the signature to the specified file instead of stdout.') + parser.add_argument( + '--algorithm', '-a', dest='algorithm', help='Signing algorithm (default: %(default)s)', + action='store', choices=['ecdsa', 'ed25519'], default='ecdsa', + ) - args = parser.parse_args() + args = parser.parse_args(argv) return args -if __name__ == '__main__': - args = parse_args() - private_key = SigningKey.from_pem(args.private_key.read()) - data = args.infile.read() +def sign_with_ecdsa(private_key_file: BinaryIO, input_file: BinaryIO, output_file: BinaryIO) -> int: + private_key = SigningKey.from_pem(private_key_file.read()) + data = input_file.read() signature = private_key.sign(data, hashfunc=hashlib.sha256) - args.outfile.write(signature) + output_file.write(signature) + return 0 + + +def sign_with_ed25519(private_key_file: BinaryIO, input_file: BinaryIO, output_file: BinaryIO) -> int: + private_key = load_pem_private_key(private_key_file.read(), password=None) + data = input_file.read() + signature = private_key.sign(data) + output_file.write(signature) + return 0 + + +def main(argv=None) -> int: + args = parse_args(argv) + if args.algorithm == 'ecdsa': + return sign_with_ecdsa(args.private_key, args.infile, args.outfile) + if args.algorithm == 'ed25519': + return sign_with_ed25519(args.private_key, args.infile, args.outfile) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/bootloader/hash.py b/scripts/bootloader/hash.py old mode 100644 new mode 100755 index 3581c0d9989e..3405aa4d8a0a --- a/scripts/bootloader/hash.py +++ b/scripts/bootloader/hash.py @@ -4,11 +4,20 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +""" +Hash content of a file. +""" +import argparse import hashlib import sys -import argparse -from intelhex import IntelHex + +from intelhex import IntelHex # type: ignore[import-untyped] + +HASH_FUNCTION_FACTORY = { + 'sha256': hashlib.sha256, + 'sha512': hashlib.sha512, +} def parse_args(): @@ -17,20 +26,37 @@ def parse_args(): formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False) - parser.add_argument('--infile', '-i', '--in', '-in', required=True, - help='Hash the contents of the specified file. If a *.hex file is given, the contents will ' - 'first be converted to binary, with all non-specified area being set to 0xff. ' - 'For all other file types, no conversion is done.') - return parser.parse_args() + parser.add_argument( + '--infile', '-i', '--in', '-in', required=True, + help='Hash the contents of the specified file. If a *.hex file is given, the contents will ' + 'first be converted to binary, with all non-specified area being set to 0xff. ' + 'For all other file types, no conversion is done.' + ) + parser.add_argument( + '--type', '-t', dest='hash_function', help='Hash function (default: %(default)s)', + action='store', choices=HASH_FUNCTION_FACTORY.keys(), default='sha256' + ) + return parser.parse_args() -if __name__ == '__main__': - args = parse_args() - if args.infile.endswith('.hex'): - ih = IntelHex(args.infile) +def generate_hash_digest(file: str, hash_function: str) -> bytes: + if file.endswith('.hex'): + ih = IntelHex(file) ih.padding = 0xff # Allows hashing with empty regions to_hash = ih.tobinstr() else: - to_hash = open(args.infile, 'rb').read() - sys.stdout.buffer.write(hashlib.sha256(to_hash).digest()) + to_hash = open(file, 'rb').read() + + hash_function = HASH_FUNCTION_FACTORY[hash_function] + return hash_function(to_hash).digest() + + +def main(): + args = parse_args() + sys.stdout.buffer.write(generate_hash_digest(args.infile, args.hash_function)) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/bootloader/keygen.py b/scripts/bootloader/keygen.py old mode 100644 new mode 100755 index 871e4fd44db9..1d88b1d47949 --- a/scripts/bootloader/keygen.py +++ b/scripts/bootloader/keygen.py @@ -4,16 +4,22 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +from __future__ import annotations -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.serialization import load_pem_private_key as load_pem -from hashlib import sha256 import argparse import sys +from hashlib import sha256, sha512 +from typing import BinaryIO +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.asymmetric import ed25519 +from cryptography.hazmat.primitives.serialization import load_pem_private_key -def generate_legal_key(): + +def generate_legal_key_for_elliptic_curve(): """ Ensure that we don't have 0xFFFF in the hash of the public key of the generated keypair. @@ -23,8 +29,8 @@ def generate_legal_key(): while True: key = ec.generate_private_key(ec.SECP256R1()) public_bytes = key.public_key().public_bytes( - encoding=serialization.Encoding.X962, - format=serialization.PublicFormat.UncompressedPoint, + encoding=serialization.Encoding.X962, + format=serialization.PublicFormat.UncompressedPoint, ) # The digest don't contain the first byte as it denotes @@ -35,7 +41,148 @@ def generate_legal_key(): return key -if __name__ == '__main__': +def generate_legal_key_for_ed25519(): + """ + Ensure that we don't have 0xFFFF in the hash of the public key of + the generated keypair. + + :return: A key who's SHA512 digest does not contain 0xFFFF + """ + while True: + key = ed25519.Ed25519PrivateKey.generate() + public_bytes = key.public_key().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + + # The digest don't contain the first byte as it denotes + # if it is compressed/UncompressedPoint. + digest = sha512(public_bytes[1:]).digest()[:16] + if not any([digest[n:n + 2] == b'\xff\xff' for n in range(0, len(digest), 2)]): + return key + + +class EllipticCurveKeysGenerator: + """Generate private and public keys for Elliptic Curve cryptography.""" + + def __init__(self, infile: BinaryIO | None = None) -> None: + """ + :param infile: A file-like object to read the private key. + """ + if infile is None: + self.private_key = generate_legal_key_for_elliptic_curve() + else: + self.private_key = load_pem_private_key(infile.read(), password=None) + self.public_key = self.private_key.public_key() + + @property + def private_key_pem(self) -> bytes: + return self.private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption(), + ) + + def write_private_key_pem(self, outfile: BinaryIO) -> bytes: + """ + Write private key pem to file and return it. + + :param outfile: A file-like object to write the private key. + """ + if outfile is not None: + outfile.write(self.private_key_pem) + return self.private_key_pem + + @property + def public_key_pem(self) -> bytes: + return self.public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo, + ) + + def write_public_key_pem(self, outfile: BinaryIO) -> bytes: + """ + Write public key pem to file and return it. + + :param outfile: A file-like object to write the public key. + """ + outfile.write(self.public_key_pem) + return self.public_key_pem + + @staticmethod + def verify_signature(public_key, message: bytes, signature: bytes) -> bool: + try: + public_key.verify(signature, message, ec.ECDSA(hashes.SHA256())) + return True + except InvalidSignature: + return False + + @staticmethod + def sign_message(private_key, message: bytes) -> bytes: + return private_key.sign(message, ec.ECDSA(hashes.SHA256())) + + +class Ed25519KeysGenerator: + """Generate private and public keys for ED25519 cryptography.""" + + def __init__(self, infile: BinaryIO | None = None) -> None: + """ + :param infile: A file-like object to read the private key. + """ + if infile is None: + self.private_key = generate_legal_key_for_ed25519() + else: + self.private_key = load_pem_private_key(infile.read(), password=None) + self.public_key = self.private_key.public_key() + + @property + def private_key_pem(self) -> bytes: + return self.private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + ) + + def write_private_key_pem(self, outfile: BinaryIO) -> bytes: + """ + Write private key pem to file and return it. + + :param outfile: A file-like object to write the private key. + """ + outfile.write(self.private_key_pem) + return self.private_key_pem + + @property + def public_key_pem(self) -> bytes: + return self.public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + + def write_public_key_pem(self, outfile: BinaryIO) -> bytes: + """ + Write public key pem to file and return it. + + :param outfile: A file-like object to write the public key. + """ + if outfile is not None: + outfile.write(self.public_key_pem) + return self.public_key_pem + + @staticmethod + def verify_signature(public_key, message: bytes, signature: bytes) -> bool: + try: + public_key.verify(signature, message) + return True + except InvalidSignature: + return False + + @staticmethod + def sign_message(private_key, message: bytes) -> bytes: + return private_key.sign(message) + + +def main(argv=None) -> int: parser = argparse.ArgumentParser( description='Generate PEM file.', formatter_class=argparse.RawDescriptionHelpFormatter, @@ -53,21 +200,28 @@ def generate_legal_key(): type=argparse.FileType('rb'), help='Read private key from specified PEM file instead ' 'of generating it.') + parser.add_argument( + '--algorithm', '-a', help='Signing algorithm (default: %(default)s)', + required=False, action='store', choices=('ec', 'ed25519'), default='ec' + ) + + args = parser.parse_args(argv) + + if args.algorithm == 'ed25519': + ed25519_generator = Ed25519KeysGenerator(args.infile) + if args.private: + ed25519_generator.write_private_key_pem(args.out) + if args.public: + ed25519_generator.write_public_key_pem(args.out) + else: + ec_generator = EllipticCurveKeysGenerator(args.infile) + if args.private: + ec_generator.write_private_key_pem(args.out) + elif args.public: + ec_generator.write_public_key_pem(args.out) - args = parser.parse_args() - sk = (load_pem(args.infile.read(), password=None) if args.infile else generate_legal_key()) - - if args.private: - private_pem = sk.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=serialization.NoEncryption(), - ) - args.out.write(private_pem) - - if args.public: - public_pem = sk.public_key().public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - args.out.write(public_pem) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/bootloader/tests/asn1parse_test.py b/scripts/bootloader/tests/asn1parse_test.py new file mode 100644 index 000000000000..96af37cb5ed3 --- /dev/null +++ b/scripts/bootloader/tests/asn1parse_test.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +import subprocess + +from asn1parse import get_ecdsa_signature +from keygen import EllipticCurveKeysGenerator + + +def test_asn1parse_with_ecdsa_and_sha256(tmpdir): + signature_der_file = tmpdir / 'signature.bin' + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + generator = EllipticCurveKeysGenerator() + generator.write_private_key_pem(private_key_file) + generator.write_public_key_pem(public_key_file) + + input_file = tmpdir / 'input_file.txt' + message = b'Test message for key verification' + input_file.write(message) + + subprocess.run( + [ + 'openssl', 'dgst', '-sha256', '-sign', private_key_file, + '-out', signature_der_file, input_file + ], + check=True + ) + assert signature_der_file.exists() + + signature = get_ecdsa_signature(signature_der_file.open('rb').read(), clength=32) + assert len(signature) == 64 + result = subprocess.run( + [ + 'openssl', 'dgst', '-sha256', '-verify', public_key_file, + '-signature', signature_der_file, input_file + ] + ) + assert result.returncode == 0, 'Signature does not match' + + input_file.write(b'Test message to fail verification') + result = subprocess.run( + [ + 'openssl', 'dgst', '-sha256', '-verify', public_key_file, + '-signature', signature_der_file, input_file + ], + ) + assert result.returncode == 1 diff --git a/scripts/bootloader/tests/conftest.py b/scripts/bootloader/tests/conftest.py new file mode 100644 index 000000000000..e1f79775766a --- /dev/null +++ b/scripts/bootloader/tests/conftest.py @@ -0,0 +1,9 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent)) diff --git a/scripts/bootloader/tests/do_sign_test.py b/scripts/bootloader/tests/do_sign_test.py new file mode 100644 index 000000000000..7073248f9203 --- /dev/null +++ b/scripts/bootloader/tests/do_sign_test.py @@ -0,0 +1,115 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +import hashlib + +from ecdsa import BadSignatureError +from ecdsa.keys import VerifyingKey +from ecdsa.util import sigdecode_string + +from do_sign import sign_with_ecdsa, sign_with_ed25519 +from keygen import Ed25519KeysGenerator, EllipticCurveKeysGenerator + + +def verify_ecdsa_signature(public_key: VerifyingKey, message: bytes, signature: bytes) -> bool: + try: + public_key.verify(signature, message, hashlib.sha256, sigdecode=sigdecode_string) + return True + except BadSignatureError: + return False + + +def test_if_file_is_properly_signed_with_ec_key(tmpdir): + generator = EllipticCurveKeysGenerator() + private_key_file = tmpdir / 'private.pem' + generator.write_private_key_pem(private_key_file) + public_key_file = tmpdir / 'public.pem' + generator.write_public_key_pem(public_key_file) + + input_file = tmpdir / 'input_file.txt' + message = b'Test message for key verification' + input_file.write(message) + + signature_file = tmpdir / 'signature.bin' + + sign_with_ecdsa( + private_key_file=private_key_file.open('rb'), + input_file=input_file.open('rb'), + output_file=signature_file.open('wb'), + ) + + public_key = VerifyingKey.from_pem(public_key_file.open('br').read()) + signature = signature_file.open('rb').read() + assert verify_ecdsa_signature( + public_key=public_key, message=message, signature=signature + ) + + +def test_if_validation_does_not_pass_for_wrong_ec_key(tmpdir): + private_key_file = tmpdir / 'private.pem' + EllipticCurveKeysGenerator().write_private_key_pem(private_key_file) + public_key_file = tmpdir / 'public.pem' + EllipticCurveKeysGenerator().write_public_key_pem(public_key_file) + + input_file = tmpdir / 'input_file.txt' + message = b'Test message for key verification' + input_file.write(message) + + signature_file = tmpdir / 'signature.bin' + + sign_with_ecdsa( + private_key_file=private_key_file.open('rb'), + input_file=input_file.open('rb'), + output_file=signature_file.open('wb'), + ) + + public_key = VerifyingKey.from_pem(public_key_file.open('br').read()) + signature = signature_file.open('rb').read() + assert verify_ecdsa_signature( + public_key=public_key, message=message, signature=signature + ) is False + + +def test_if_validation_does_not_pass_for_wrong_ed25519_key(tmpdir): + generator = Ed25519KeysGenerator() + private_key_file = tmpdir / 'private.pem' + generator.write_private_key_pem(private_key_file) + public_key = generator.public_key + + input_file = tmpdir / 'input_file.txt' + message = b'Test message for key verification' + input_file.write(message) + + signature_file = tmpdir / 'signature.bin' + + sign_with_ed25519( + private_key_file=private_key_file.open('rb'), + input_file=input_file.open('rb'), + output_file=signature_file.open('bw') + ) + assert Ed25519KeysGenerator.verify_signature( + public_key, message, signature_file.open('br').read() + ) + + +def test_if_file_is_properly_signed_with_ed25519_key(tmpdir): + private_key_file = tmpdir / 'private.pem' + Ed25519KeysGenerator().write_private_key_pem(private_key_file) + public_key = Ed25519KeysGenerator().public_key + + input_file = tmpdir / 'input_file.txt' + message = b'Test message for key verification' + input_file.write(message) + + signature_file = tmpdir / 'signature.bin' + + sign_with_ed25519( + private_key_file=private_key_file.open('rb'), + input_file=input_file.open('rb'), + output_file=signature_file.open('bw') + ) + assert Ed25519KeysGenerator.verify_signature( + public_key, message, signature_file.open('br').read() + ) is False diff --git a/scripts/bootloader/tests/keygen_test.py b/scripts/bootloader/tests/keygen_test.py new file mode 100644 index 000000000000..4f166cccf944 --- /dev/null +++ b/scripts/bootloader/tests/keygen_test.py @@ -0,0 +1,127 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +import pytest +from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key + +from keygen import Ed25519KeysGenerator, EllipticCurveKeysGenerator + + +@pytest.mark.parametrize( + 'keys_generator', + [EllipticCurveKeysGenerator, Ed25519KeysGenerator], + ids=['ec', 'ed25519'] +) +def test_keys_generator_generates_proper_pem_key(keys_generator): + key_gen = keys_generator() + assert b'-----BEGIN PRIVATE KEY-----' in key_gen.private_key_pem + assert b'-----END PRIVATE KEY-----' in key_gen.private_key_pem + assert b'-----BEGIN PUBLIC KEY-----' in key_gen.public_key_pem + assert b'-----END PUBLIC KEY-----' in key_gen.public_key_pem + + +def test_elliptic_curve_keys_generator(tmpdir): + private_key_file = tmpdir / 'private.pem' + ec_keys_generator_1 = EllipticCurveKeysGenerator() + private_key_pem_1 = ec_keys_generator_1.write_private_key_pem(private_key_file) + public_key_pem_1 = ec_keys_generator_1.public_key_pem + # public key and private should not be the same + assert private_key_pem_1 != public_key_pem_1 + + # test if same private key is loaded from file + ec_keys_generator_2 = EllipticCurveKeysGenerator(private_key_file.open('rb')) + private_key_pem_2 = ec_keys_generator_2.private_key_pem + assert private_key_pem_1 == private_key_pem_2 + + +def test_signing_with_elliptic_curve_with_valid_keys(tmpdir): + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + generator = EllipticCurveKeysGenerator() + generator.write_private_key_pem(private_key_file) + generator.write_public_key_pem(public_key_file) + + message = b'Test message for key verification' + private_key = load_pem_private_key(private_key_file.open('br').read(), password=None) + public_key = load_pem_public_key(public_key_file.open('br').read()) + + signature = EllipticCurveKeysGenerator.sign_message(private_key, message) + assert EllipticCurveKeysGenerator.verify_signature(public_key, message, signature) + + +def test_signing_with_elliptic_curve_with_invalid_private_key(tmpdir): + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + generator = EllipticCurveKeysGenerator() + generator.write_private_key_pem(private_key_file) + + public_generator = EllipticCurveKeysGenerator() + public_generator.write_public_key_pem(public_key_file.open('wb')) + + message = b'Test message for key verification' + private_key = load_pem_private_key(private_key_file.open('br').read(), password=None) + public_key = load_pem_public_key(public_key_file.open('br').read()) + + signature = EllipticCurveKeysGenerator.sign_message(private_key, message) + assert EllipticCurveKeysGenerator.verify_signature(public_key, message, signature) is False + + +def test_ed25519_keys_generator(tmpdir): + private_key_file = tmpdir / 'private.pem' + ec_keys_generator_1 = Ed25519KeysGenerator() + private_key_pem_1 = ec_keys_generator_1.write_private_key_pem(private_key_file) + public_key_pem_1 = ec_keys_generator_1.public_key_pem + assert private_key_pem_1 != public_key_pem_1 + + # test if same private key is loaded from file + ec_keys_generator_2 = Ed25519KeysGenerator(private_key_file.open('rb')) + private_key_pem_2 = ec_keys_generator_2.private_key_pem + assert private_key_pem_1 == private_key_pem_2 + + +def test_signing_with_ed25519_with_valid_keys(tmpdir): + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + generator = Ed25519KeysGenerator() + generator.write_private_key_pem(private_key_file.open('wb')) + generator.write_public_key_pem(public_key_file.open('wb')) + + message = b'Test message for key verification' + private_key = load_pem_private_key(private_key_file.open('br').read(), password=None) + public_key = load_pem_public_key(public_key_file.open('br').read()) + signature = Ed25519KeysGenerator.sign_message(private_key, message) + assert Ed25519KeysGenerator.verify_signature(public_key, message, signature) + + +def test_signing_with_ed25519_with_valid_keys_and_private_key_from_public_pem_file(tmpdir): + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + private_generator = Ed25519KeysGenerator() + private_generator.write_private_key_pem(private_key_file.open('wb')) + + public_generator = Ed25519KeysGenerator(private_key_file.open('rb')) + public_generator.write_public_key_pem(public_key_file.open('wb')) + + message = b'Test message for key verification' + private_key = load_pem_private_key(private_key_file.open('br').read(), password=None) + public_key = load_pem_public_key(public_key_file.open('br').read()) + signature = Ed25519KeysGenerator.sign_message(private_key, message) + assert Ed25519KeysGenerator.verify_signature(public_key, message, signature) + + +def test_signing_with_ed25519_signature_with_invalid_private_key(tmpdir): + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + private_generator = Ed25519KeysGenerator() + private_generator.write_private_key_pem(private_key_file.open('wb')) + + public_generator = Ed25519KeysGenerator() + public_generator.write_public_key_pem(public_key_file.open('wb')) + + message = b'Test message for key verification' + private_key = load_pem_private_key(private_key_file.open('br').read(), password=None) + public_key = load_pem_public_key(public_key_file.open('br').read()) + signature = Ed25519KeysGenerator.sign_message(private_key, message) + assert Ed25519KeysGenerator.verify_signature(public_key, message, signature) is False diff --git a/scripts/bootloader/tests/validation_data_test.py b/scripts/bootloader/tests/validation_data_test.py new file mode 100644 index 000000000000..272e523a768f --- /dev/null +++ b/scripts/bootloader/tests/validation_data_test.py @@ -0,0 +1,75 @@ +# +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +import hashlib +from pathlib import Path + +import ecdsa + +from do_sign import sign_with_ed25519, sign_with_ecdsa +from hash import generate_hash_digest +from keygen import Ed25519KeysGenerator, EllipticCurveKeysGenerator +from validation_data import Ed25519SignatureValidator, EcdsaSignatureValidator + + +def test_data_validation_for_ec(tmpdir): + magic_value = '0x281ee6de,0x86518483,79362' + offset = 0 + private_key_file = tmpdir / 'private.pem' + public_key_file = tmpdir / 'public.pem' + message_file = Path(__file__).parent / 'zephyr.hex' + message_signature_file = tmpdir / 'signed.hex' + output_hex_file = tmpdir / 'output.hex' + hashed_file = tmpdir / 'hashed.hex' + + keys_generator = EllipticCurveKeysGenerator() + keys_generator.write_private_key_pem(private_key_file) + keys_generator.write_public_key_pem(public_key_file) + + hashed_file.open('wb').write(generate_hash_digest(str(message_file), 'sha256')) + + sign_with_ecdsa( + private_key_file.open('rb'), hashed_file.open('rb'), message_signature_file.open('wb') + ) + + public_key = ecdsa.VerifyingKey.from_pem(public_key_file.read()) + EcdsaSignatureValidator(hashfunc=hashlib.sha256).append_validation_data( + signature=message_signature_file.open('rb').read(), + input_file=message_file.open('r'), + public_key=public_key, + offset=offset, + output_hex=output_hex_file.open('w'), + output_bin=None, + magic_value=magic_value + ) + + +def test_data_validation_for_ed25519(tmpdir): + magic_value = '0x281ee6de,0x86518483,79362' + offset = 0 + private_key_file = tmpdir / 'private.pem' + message_file = Path(__file__).parent / 'zephyr.hex' + hashed_file = tmpdir / 'hashed.hex' + message_signature_file = tmpdir / 'signed.hex' + output_hex_file = tmpdir / 'output.hex' + + keys_generator = Ed25519KeysGenerator() + keys_generator.write_private_key_pem(private_key_file) + + hashed_file.open('wb').write(generate_hash_digest(str(message_file), 'sha512')) + + sign_with_ed25519( + private_key_file.open('rb'), hashed_file.open('rb'), message_signature_file.open('wb') + ) + + Ed25519SignatureValidator(hashfunc=hashlib.sha512).append_validation_data( + signature=message_signature_file.open('rb').read(), + input_file=message_file.open('r'), + public_key=keys_generator.public_key, + offset=offset, + output_hex=output_hex_file.open('w'), + output_bin=None, + magic_value=magic_value + ) diff --git a/scripts/bootloader/tests/zephyr.hex b/scripts/bootloader/tests/zephyr.hex new file mode 100644 index 000000000000..fb4f3a3d3c07 --- /dev/null +++ b/scripts/bootloader/tests/zephyr.hex @@ -0,0 +1,1497 @@ +:1098000050110020EDA60000E5DE0000D9A6000002 +:10981000D9A60000D9A60000D9A60000D9A600004C +:1098200000000000000000000000000099A80000F7 +:10983000D9A600000000000059A80000D9A6000029 +:1098400001AA000001AA000001AA000001AA00006C +:1098500001AA000001AA000001AA000001AA00005C +:1098600001AA000001AA000001AA000001AA00004C +:1098700001AA000001AA000001AA000001AA00003C +:1098800001AA000001AA000001AA000001AA00002C +:1098900001AA000001AA000001AA000001AA00001C +:1098A00001AA000001AA000001AA000001AA00000C +:1098B00001AA000001AA000001AA000001AA0000FC +:1098C00001AA000001AA000001AA000001AA0000EC +:1098D00001AA000001AA000001AA000001AA0000DC +:1098E00001AA000001AA000001AA000001AA0000CC +:1098F00001AA000001AA000001AA000001AA0000BC +:1099000001AA000001AA000001AA000001AA0000AB +:1099100001AA000001AA000001AA000001AA00009B +:1099200001AA000001AA000001AA000001AA00008B +:1099300001AA000001AA000001AA000001AA00007B +:1099400001AA000001AA000001AA000001AA00006B +:1099500001AA000001AA000001AA000001AA00005B +:1099600001AA000001AA000001AA000001AA00004B +:1099700001AA000001AA000001AA000001AA00003B +:1099800001AA000001AA000001AA000001AA00002B +:1099900001AA000001AA000001AA000001AA00001B +:1099A00001AA000001AA000001AA000001AA00000B +:1099B00001AA000001AA000001AA000001AA0000FB +:1099C00001AA000001AA000001AA000001AA0000EB +:1099D00001AA000001AA000001AA000001AA0000DB +:1099E00001AA000001AA000001AA000001AA0000CB +:1099F00001AA000001AA000001AA000001AA0000BB +:109A000001AA000001AA000001AA000001AA0000AA +:109A100001AA000001AA000001AA000001AA00009A +:109A200001AA000001AA000001AA000001AA00008A +:109A300001AA000001AA000001AA000001AA00007A +:109A400001AA000001AA000001AA000001AA00006A +:109A500001AA000001AA000001AA000001AA00005A +:109A600001AA000001AA000001AA000001AA00004A +:109A700001AA000001AA000001AA000001AA00003A +:109A800001AA000001AA000001AA000001AA00002A +:109A900001AA000001AA000001AA000001AA00001A +:109AA00001AA000001AA000001AA000001AA00000A +:109AB00001AA000001AA000001AA000001AA0000FA +:109AC00001AA000001AA000001AA000001AA0000EA +:109AD00001AA000001AA000001AA000001AA0000DA +:109AE00001AA000001AA000001AA000001AA0000CA +:109AF00001AA000001AA000001AA000001AA0000BA +:109B000001AA000001AA000001AA000001AA0000A9 +:109B100001AA000001AA000001AA000001AA000099 +:109B200001AA000001AA000001AA000001AA000089 +:109B300001AA000001AA000001AA000001AA000079 +:109B400001AA000001AA000001AA000001AA000069 +:109B500001AA000001AA000001AA000001AA000059 +:109B600001AA000001AA000001AA000001AA000049 +:109B700001AA000001AA000001AA000001AA000039 +:109B800001AA000001AA000001AA000001AA000029 +:109B900001AA000001AA000001AA000001AA000019 +:109BA00001AA000001AA000001AA000001AA000009 +:109BB00001AA000001AA000001AA000001AA0000F9 +:109BC00001AA000001AA000001AA000001AA0000E9 +:109BD00001AA000001AA000001AA000001AA0000D9 +:109BE00001AA000001AA000001AA000001AA0000C9 +:109BF00001AA000001AA000001AA000001AA0000B9 +:109C000001AA000001AA000001AA000001AA0000A8 +:109C100001AA000001AA000001AA000001AA000098 +:109C200001AA000001AA000001AA000001AA000088 +:109C300001AA000001AA000001AA000001AA000078 +:109C400001AA000001AA000001AA000001AA000068 +:109C500001AA000001AA000001AA000001AA000058 +:109C600001AA000001AA000001AA000001AA000048 +:109C700001AA000001AA000001AA000000000000E3 +:109C800000000000000000000000000000000000D4 +:109C900000000000000000000000000000000000C4 +:109CA00000000000000000000000000000000000B4 +:109CB00000000000000000000000000000000000A4 +:109CC0000000000000000000000000000000000094 +:109CD0000000000000000000000000000000000084 +:109CE0000000000000000000000000000000000074 +:109CF0000000000000000000000000000000000064 +:109D00000000000000000000000000000000000053 +:109D10000000000000000000000000000000000043 +:109D20000000000000000000000000000000000033 +:109D30000000000000000000000000000000000023 +:109D40000000000000000000000000000000000013 +:109D50000000000000000000000000000000000003 +:109D600000000000000000000000000000000000F3 +:109D700000000000000000000000000000000000E3 +:109D800000000000000000000000000000000000D3 +:109D900000000000000000000000000000000000C3 +:109DA00000000000000000000000000000000000B3 +:109DB00000000000000000000000000000000000A3 +:109DC0000000000000000000000000000000000093 +:109DD0000000000000000000000000000000000083 +:109DE0000000000000000000000000000000000073 +:109DF0000000000000000000000000000000000063 +:109E0000DEE61E284CBBCE8F023600003C00000070 +:109E10001A5D00000100000000980000009800009A +:109E2000FFFF0291000000000000000000000000A1 +:0C9E300000000000000000000000000026 +:109E3C0053B94AB9002908BF00281CBF4FF0FF31A5 +:109E4C004FF0FF3000F00CB8ADF1080C6DE904CE0A +:109E5C0000F008F8DDF804E0DDE9022304B07047F7 +:109E6C00704700BF2DE9F04786468C460846089E91 +:109E7C00154674461946002B40F0C1808542B2FA53 +:109E8C0082F245D94AB1C2F1200300FA02FC954096 +:109E9C0094402EFA03F343EA0C0C4FEA154EA8B289 +:109EAC00230CBCFBFEF80EFB18CC08FB00F943EAB4 +:109EBC000C43994528D9EB1808F1FF3704D2994582 +:109ECC0002D9A8F102072B44A3EB0903A4B2B3FBFC +:109EDC00FEFC0EFB1C330CFB00F044EA0344A042D6 +:109EEC0014D92C190CF1FF3304D2A04202D9ACF1D5 +:109EFC0002032C44241A43EA07401EB1D440002329 +:109F0C0034607360BDE8F0874746DDE76346F1E7F0 +:109F1C0092BB431B01214FEA154EAFB2200CB3FB91 +:109F2C00FEFC0EFB1C330CFB07F840EA0343984580 +:109F3C0061D9EB180CF1FF3004D2984502D9ACF181 +:109F4C0002002B44A3EB0803A4B2B3FBFEFC0EFBF4 +:109F5C001C330CFB07F744EA0344A7424DD92C19D8 +:109F6C000CF1FF3304D2A74202D9ACF102032C440A +:109F7C00E41B43EA0040C0E7C2F120039540944043 +:109F8C0020FA03F12EFA03F34FEA154E9040AFB2CC +:109F9C000343B1FBFEFC0EFB1C10190C0CFB07F869 +:109FAC0041EA0041884522D969180CF1FF3004D2EE +:109FBC00884502D9ACF102002944A1EB08019BB2FF +:109FCC00B1FBFEFC0EFB1C110CFB07F743EA014333 +:109FDC009F420ED9EB180CF1FF3104D29F4202D9EB +:109FEC00ACF102012B44DB1B41EA004193E76046D4 +:109FFC00E3E76146F7E76046A4E76346B8E78342C8 +:10A00C0006D916B9002108467CE7C6E900E0F9E755 +:10A01C00B3FA83F78FB9834201D3724508D8BEEBEC +:10A02C00020460EB030301209C461EB9002169E782 +:10A03C003846FAE7C6E9004CF8E7C7F1200CBB40FC +:10A04C000EFA07F522FA0CF42EFA0CF1BA401C4366 +:10A05C0020FA0CF3B8404FEA144901431FFA84FE6E +:10A06C00B3FBF9F809FB18300B0C08FB0EFA43EAAA +:10A07C0000439A4540D9E31808F1FF3004D29A45C1 +:10A08C0002D9A8F102002344A3EB0A0389B2B3FB63 +:10A09C00F9F809FB183308FB0EFE41EA03418E4523 +:10A0AC002CD9611808F1FF3304D28E4502D9A8F1DE +:10A0BC000203214443EA0040A1EB0E01A0FB0298ED +:10A0CC004145CE46434602D306D14D4504D2013814 +:10A0DC00B9EB020E68EB0403002EA7D0B5EB0E0211 +:10A0EC0061EB030101FA0CFC22FA07F3F9404CEA8C +:10A0FC000303C6E9003199E74046C5E74346D9E773 +:10A10C0008B50349034803F0A9FA002008BD00BFB5 +:10A11C0048F2000063F20000014B1B68184700BFB7 +:10A12C0000000020014B1860704700BF00000020A9 +:10A13C0030B585B004460D4610220021684604F067 +:10A14C00F6FA02238DF80230044B2A462146684663 +:10A15C00019303F095FA05B030BD00BF25A10000B6 +:10A16C0080B505460E461746984603F091F803460F +:10A17C0004F0E6FA0446DFF81890424649F800303D +:10A18C0039463046A84754F8090000F011FB00BFCF +:10A19C00080000002DE9F047058B0446280790467F +:10A1AC0011D4022905F0070540F0CC80204603F0BD +:10A1BC00B1FD78B10138218B042811D8DFE800F00B +:10A1CC00C2106D85950001290CBF45F0100545F0B6 +:10A1DC002005258388F31188BFF36F8FBDE8F087C6 +:10A1EC0001F0070AAA4500F0A5800027B9463E46B3 +:10A1FC00A368002B00F0BC8041F00801218388F398 +:10A20C001188BFF36F8FA168002900F0BA800D6828 +:10A21C00002900F0B680334652462046D1F804801F +:10A22C00C047002D76D02B6829461D46F0E701F07B +:10A23C0007035A1F012A1BD8002221F00701062B05 +:10A24C00276889B2C4E9002209D13B4603E0628B3E +:10A25C00013262831B68002BF9D141F002012046C8 +:10A26C00218303F057FD00287AD041F0200176E0DD +:10A27C00042B0AD121F0070189B22046218303F077 +:10A28C0049FD10B141F020012183218B01F0070A17 +:10A29C00AA454FD00027B946AAE7236921F0070148 +:10A2AC0041F00601062DD3F8009021832DD1B9F190 +:10A2BC00000F3FD041F00801218388F31188BFF3D0 +:10A2CC006F8F20463949C84729E021F007012369DF +:10A2DC0041F0040189B2042DD3F804902183E6D017 +:10A2EC0000274FF0040A3E4682E721F0070123695C +:10A2FC0041F0050189B2052DD3F808902183D6D001 +:10A30C0000274FF0050A3E4672E700274FF0060A79 +:10A31C003E466DE72B4687E7B9F1000FD1D1EFF33D +:10A32C001188202383F31288BFF36F8F238B23F0C4 +:10A33C0008032383258BEA062ED525F010032383EF +:10A34C0005F007056669218B002EBFF670AF002360 +:10A35C002768C4E9003321F0070141F00101218392 +:10A36C00218B4FF0000901F0070AAA457FF440AF9A +:10A37C00002F9CD041F00801218388F31188BFF392 +:10A38C006F8F002FC8D03D68394633465246204661 +:10A39C0003F0ECFC2F46F4E7AB067FF51BAF25F082 +:10A3AC002003238305F0070500E700BFA5DD0000AF +:10A3BC00014B036000207047D8F0000008B504483A +:10A3CC0003F09BFCBDE80840024803F096BC00BFBC +:10A3DC0074F20000AAF2000030BFFFF7FDBF00BF0F +:10A3EC002DE9F04105460B4EDFF82C80324606EB8A +:10A3FC000807CC1B2046094904F08CF9A0194246E9 +:10A40C000021083C04F093F92C6707F10800BDE823 +:10A41C00F08100BF00000000040000008CF0000080 +:10A42C00074A936893F90E10002907DB526993428F +:10A43C001FBF044A536843F080535360704700BFFA +:10A44C00D803002000ED00E0154B9A6A12F0007F53 +:10A45C009A6A14BF1E201D2012F0807F9A6A18BFC2 +:10A46C001F2012F4801F9A6A18BF022012F4002FCA +:10A47C009A6A18BF212012F4802F9A6A18BF2220E2 +:10A48C0012F4003F9A6A18BF232012F4803F9A6A94 +:10A49C0018BF24206FEA02426FEA12429A62704798 +:10A4AC0000ED00E010B5194B0246986A9C6A10F456 +:10A4BC00805F14BF1720162014F4006F9C6A18BF1D +:10A4CC001820A40509D5986B986A000404D51AB114 +:10A4DC009A6A22F400429A6219200C4B9A6A12F47E +:10A4EC00806F9A6A18BF1A20D2050CD49B6A13F499 +:10A4FC00005F18BF1C20054A936A43F47F439362A4 +:10A50C0000230B7010BD1B20F5E700BF00ED00E031 +:10A51C0010B51C4B0246986A9C6A10F0100F14BFC1 +:10A52C001120102014F0080F9C6A18BF1220A407E9 +:10A53C0009D5586B986A000604D51AB19A6A22F0AC +:10A54C0080029A6213200F4B9A6A12F0010F9A6ADA +:10A55C0018BF142012F0200F9A6A18BF1520D406C9 +:10A56C0058BF9B6A074B9A6A920642BF5A6A22F4FA +:10A57C0000525A629A6A42F0FF029A6200230B70F0 +:10A58C0010BD00BF00ED00E0F0B5474B04465B6822 +:10A59C0000258BB0C3F3080385F31188BFF36F8FCD +:10A5AC0002F07F40B0F17F4F11D102F00C0008286F +:10A5BC000DD010074CBF0C4601250022033B8DF833 +:10A5CC000720032B05D8DFE803F02462665F2C46D6 +:10A5DC00F3E700239DF80720CAB902AE04F1200767 +:10A5EC00324620686168083403C2BC421646F7D173 +:10A5FC00099A002D53D0C2F3080121B96FEA5222F7 +:10A60C006FEA42220992184602A903F05BFC0BB0D8 +:10A61C00F0BD254BDA6A9107DBD4DA6A002AD8DB65 +:10A62C00DA6A5200D5D55A69A16942F480725A612E +:10A63C00BFF34F8FBFF36F8F5A6931F8021C22F4AE +:10A64C0080725A61BFF34F8FBFF36F8F4DF602725A +:10A65C00914209D09A6AD2B242B101200DF10701A0 +:10A66C00FFF756FF0346B5E72368B3E79A6A12F47F +:10A67C007F4F05D001200DF10701FFF713FFF1E724 +:10A68C009B6AB3F5803FA4D3FFF7DEFEEAE7002018 +:10A69C000DF10701E4E700200DF10701EDE722F4CD +:10A6AC00FF7222F00102ADE700ED00E0064B5A69A3 +:10A6BC0042F010025A615A6942F480625A615A6936 +:10A6CC0022F008025A61704700ED00E0EFF30880B9 +:10A6DC00EFF3098101B57246FFF756FF01BD00BFCC +:10A6EC00002080F31488BFF36F8F002080F30A885A +:10A6FC0080F30B8801F08EF900200E490860BFF33F +:10A70C004F8F0D4880F3088800F02CF8202080F340 +:10A71C0011880A484FF40061401880F30988EFF360 +:10A72C0014800221084380F31488BFF36F8F00F06C +:10A73C00EFF8000094ED00E0501100201004002010 +:10A74C00002318460649D1F89020C2F30722934201 +:10A75C0000DB7047C1F89830C1F8A0000133F6E770 +:10A76C0000ED00E010B572B6002484F31388FFF7F7 +:10A77C00E7FF23464FF0FF300C4A03F12001013371 +:10A78C00102B42F82100F8D100234FF0FF30074A7C +:10A79C0003F160010133102B42F82100F8D162B6AD +:10A7AC00BFF34F8FBFF36F8F10BD00BF00E100E010 +:10A7BC00084B9B6883420BD1EFF3058343B1064BE7 +:10A7CC005A6842F080525A605A6A22F400425A6225 +:10A7DC0003F0F3BED803002000ED00E06FF00A0197 +:10A7EC00084A93689967084958674B6843F0805347 +:10A7FC004B60002383F31188BFF36F8F9368986FBE +:10A80C00704700BFD803002000ED00E0EFF3118388 +:10A81C00202383F31288BFF36F8F4FF00061084A37 +:10A82C0053699360074A5160196F074A1160002100 +:10A83C005A6F596782F31188BFF36F8F704700BF4F +:10A84C00D803002000ED00E0100300200E498A68B8 +:10A85C004FF030001044EFF3098C80E8F01F744681 +:10A86C007046FFF7D3FFA64607498A6802F130000D +:10A87C0090E8F01F8CF3098802F1000004B503F096 +:10A88C0036FBBDE804407047D80300201EF0040FCF +:10A89C000CBFEFF30880EFF30980816911F8021CFB +:10A8AC000229FFD001B503F013FB01BD00B2002853 +:10A8BC0007DB01234109034A00F01F00834042F8E3 +:10A8CC002130704700E100E0054B420953F822208B +:10A8DC00012300F01F0003FA00F01040704700BF86 +:10A8EC0000E100E000B2002801F10101ACBF00F171 +:10A8FC006040064B4FEA4111C9B2ABBF00F5614055 +:10A90C0000F00F0080F800131954704714ED00E0AC +:10A91C000B4A08B50B4B22F07F029A60BFF34F8FA6 +:10A92C00BFF36F8FD3F8882022F47002C3F888200D +:10A93C0002F01AF902F038FD00F04CF802F054F96C +:10A94C000098000000ED00E042F8203C009B0B4911 +:10A95C0042F81C3C019B21F0010142F8183C029B7F +:10A96C0042F8081C42F8143C4FF0807342F8043C47 +:10A97C000023203A02654367704700BF6DA10000B9 +:10A98C0000B50D4B15469860026F0C4B0E461A60C5 +:10A99C00036E83F30B882C4686F309884FF0000076 +:10A9AC00074B984720464FF000014FF000024FF044 +:10A9BC000003044C204700BFD803002010030020E4 +:10A9CC0003DF00006DA100001022014B1A617047DB +:10A9DC0000ED00E00023202040F20F1210B5044CD3 +:10A9EC00E1180133934281F80003F9D110BD00BF87 +:10A9FC0000E100E008B5EFF30583064A103B02EBDB +:10AA0C00C30152F8330049688847BDE80840FFF796 +:10AA1C0007BD00BF14E800001FB5094C094A2346C6 +:10AA2C000121094800F0FAF800230393074B012198 +:10AA3C00E41A01A80193029400F0FAF804B010BDD6 +:10AA4C0000F0022000000020D8F000006801002077 +:10AA5C0010B50C7A0B6804F01F0223F01F031A4385 +:10AA6C00CB68640923F01F03054943EA440343F010 +:10AA7C000103C1F89800C1F89C20C1F8A03010BDAA +:10AA8C0000ED00E010B5074BD3F89030C3F307236B +:10AA9C00013B984202D8FFF7DBFF10BD6FF01500A9 +:10AAAC00FBE700BF00ED00E02DE9F04F0F46144628 +:10AABC0006464FF000084D4D85B0B84580F29280A7 +:10AACC00D6F80490B9F1000F36D0B9F11F0F40F24F +:10AADC00878019F01F0F40F08380D6F800A01AF081 +:10AAEC001F0F7DD1504603F029FA834609EB0A006B +:10AAFC00013803F023FA834572D11BF1160F6FD086 +:10AB0C005C456DDDC5F898B0D5F89C10C5F898B0CB +:10AB1C00D5F8A03021F01F0143F01F0AD6E900320E +:10AB2C001A44994202F1FF391ED1CA4508D1314667 +:10AB3C005FFA8BF003F00BFA08F101080C36BCE756 +:10AB4C003146C5F898B0D5F89C3022F01F0203F0BE +:10AB5C001F031343C5F89C30E0B203F0F8F910F171 +:10AB6C00160F3DD0441CE7E7C5F898B0D5F8A020E7 +:10AB7C00013B23F01F0302F01F021343CA45314669 +:10AB8C00E0B2C5F8A030E8D003F0E1F910F1160FEF +:10AB9C0026D0C5F898B0D5F89C209DF808300AF15D +:10ABAC00FF3A62F304038DF80830D5F8A0309DF815 +:10ABBC0008205B0863F347128DF80820D6E90032B1 +:10ABCC001344009323F01F035344A3EB09030130F8 +:10ABDC0023F01F036946C0B20393FFF753FFBEE790 +:10ABEC006FF01504204605B0BDE8F08F00ED00E0D5 +:10ABFC000522044BC3F89420BFF34F8FBFF36F8F24 +:10AC0C00704700BF00ED00E0BFF35F8F0022024BE6 +:10AC1C00C3F89420704700BF00ED00E010B5034C62 +:10AC2C002278FFF741FF207010BD00BF0804002000 +:10AC3C002DE9F74F294D8A4606464FF00008AB46E2 +:10AC4C00274FD0451BDAD6F80490B9F1000F3FD04E +:10AC5C0031680846019103F071F90199044609EB3A +:10AC6C000100013803F06AF984424FF014021D4BC5 +:10AC7C0008D002FB08F36FF015024BF8032003B069 +:10AC8C00BDE8F08F14F1160F2C60F8D01978A142A2 +:10AC9C00F5DD02FB08B0C7F89840C7F89840D7F824 +:10ACAC009C4000F10801007B64F304000871D7F8A4 +:10ACBC00A0000C79400860F347140C71D7F89C1075 +:10ACCC0021F01F016960D7F8A01021F01F01296144 +:10ACDC0008F1010814350C36B3E700BF140300204B +:10ACEC0000ED00E008040020F0B54149414CD1F8DA +:10ACFC0090302568C3F307239D4287B075D8002098 +:10AD0C00FFF782FF3C4BC1F8C030854216D13B4C5B +:10AD1C0001A82570FFF74CFB00250646384FAE42C4 +:10AD2C0013D1002023783249D1F89020C2F30722A6 +:10AD3C00934254DBFFF75CFF00202FE0616801EBCE +:10AD4C000011FFF785FE0130DFE70199280101EBC7 +:10AD5C000513DA683A40B2F5001F21D0B2F5800F26 +:10AD6C002BD0B2F5801F38D15A689B680958013B2B +:10AD7C0022F01F0003440120844623F01F0340EA05 +:10AD8C004C10CDE902218DF8100002A92078059312 +:10AD9C00FFF778FE00281DDA6FF0150007B0F0BD44 +:10ADAC005A689B680958013B22F01F0003444FF07E +:10ADBC00020C012023F01F03E1E75A689B68095835 +:10ADCC00013B22F01F0003444FF0000C062023F03F +:10ADDC001F03D4E723780133237001359FE7C1F8B3 +:10ADEC009830C1F8A00001339EE74FF0FF30D5E753 +:10ADFC0000ED00E0E4F00000AAFF4400080400208D +:10AE0C000000F0FF08B5024B1B689847002008BDF6 +:10AE1C0028030020034B044A1860937843F0020384 +:10AE2C0093707047280300200400002008B50649E1 +:10AE3C00064821F00701C1F1005202F53C3202F044 +:10AE4C0002FF002008BD00BF571100202C0300207A +:10AE5C0008B500F0C9FD00F05FF841F6F873214821 +:10AE6C00D0F82416C1F3080203EA5133132182F4FB +:10AE7C008072883201FB02331B495A0ADB0548BF3A +:10AE8C000132C1F80429D0F820364FF47A60C3F3AC +:10AE9C000802C3F30943434342F21C5082F480720C +:10AEAC0002F2172200FB02334FF47A701B0AB3FB39 +:10AEBC00F0F200FB1233B3F5FA7F28BF0132094BD5 +:10AECC00C1F81C27D3F83423084B00209A424FF0CA +:10AEDC0001029CBF064BC1F84036034BC3F8002659 +:10AEEC0008BD00BF00C0FF0000001250001D0A1872 +:10AEFC0040E0A91E48B1054B054A1B68B3FBF2F3B1 +:10AF0C005843044B43F001031847704720000020BE +:10AF1C0040420F00C0F000000122024BC3F8042491 +:10AF2C00704700BF002008E0084B70B5C31A0D46EF +:10AF3C0007499B10044659434022064803F02EF85B +:10AF4C002B4601462046BDE8704018474C030020B4 +:10AF5C00B76DDBB6B0E70000402373B50B4C009324 +:10AF6C00041B0B460D460A49A41061430646094AC8 +:10AF7C00094803F03FF8011E05DA30462B4602B0B3 +:10AF8C00BDE87040184702B070BD00BF4C030020F4 +:10AF9C00B76DDBB66BE00000B0E7000010B5EFF367 +:10AFAC001184202383F31288BFF36F8F074A136831 +:10AFBC0023F00201DB07116002D4012003F046F9F3 +:10AFCC0084F31188BFF36F8F10BD00BF9C0300206A +:10AFDC0070B50022012104464FF48770FFF782FC04 +:10AFEC000F4800F0FDFD0F4B984215D100F008FE04 +:10AFFC0026690D49304602F0DCFE00280BDB0125EA +:10B00C0035642469084904F11C0002F0D2FE0028C2 +:10B01C0001DB0020E56470BD6FF00400FBE700BFAE +:10B02C0031B100000000AD0B10F100001FB5002283 +:10B03C000123CDE902236B46084ACDF800D0CDF8A8 +:10B04C0004D003F005F8002805DB43F60952002371 +:10B05C00684601F059FE05B05DF804FB89E000007C +:10B06C000C22104B014602FB0033002030B4D3E914 +:10B07C000E529863EFF31180202484F31288BFF3EF +:10B08C006F8F1C6C04F0C00444F002041C6480F349 +:10B09C001188BFF36F8F1DB12B4630BC0248184787 +:10B0AC0030BC70474C030020B0E7000010B4EFF345 +:10B0BC001182202383F31288BFF36F8F1648016827 +:10B0CC0041F002040460C80707D482F31188BFF36F +:10B0DC006F8F012010BC00F0A9BD1049D1F80C14E1 +:10B0EC00C903F2D5EFF3118083F31288BFF36F8F8E +:10B0FC000B490B6C03F0C00343F002030B6480F3A9 +:10B10C001188BFF36F8F82F31188BFF36F8F00200C +:10B11C0010BCFFF7A5BF00BF9C03002000E010503F +:10B12C004C03002010B1012804D07047034B1B6C5A +:10B13C005B0701D1FFF794BF704700BF4C030020A1 +:10B14C002DE9F041012207463449D1E8EF3FC1E82F +:10B15C00E02F0028F9D133B9314932484B60CB602C +:10B16C008A6002F039FE7B1E012B28D8012F06D1F4 +:10B17C002D4BD3F8503403F00303012B1FD003F0F5 +:10B18C0071F90546E8B9294B1B78D3B10422064660 +:10B19C00254BC3F80823244CDFF89480D4F84C24B6 +:10B1AC00D4F84C34D20315D503F00303012B01D092 +:10B1BC00012F0FD16DB386F31188BFF36F8FBDE8EC +:10B1CC00F081EFF31186202383F31288BFF36F8F86 +:10B1DC000125E0E7C5B1304602F0A1FED4F84034B9 +:10B1EC0013F0FF03DAD1D4F80821002AD6D0C4F822 +:10B1FC000831D4F808314FF480420123C4F84034AC +:10B20C00C8F8A0212361C9E70021202002F020F812 +:10B21C00E4E70422044BC3F80423D0E748030020DE +:10B22C00380300206803002000E010500B040020BD +:10B23C0000E100E008B5074803F0D6F838B106483D +:10B24C00FFF7E8FD0448FEF76DFF002008BD6FF026 +:10B25C001200FBE700E8000069B200000A2838B5CC +:10B26C000446074D04D1AB680D2128465B6898470E +:10B27C00AB6803485B68E1B29847204638BD00BF15 +:10B28C0000E8000038B545682B69E3B11035284655 +:10B29C0001F032F90446B0B90146284601F0EEF847 +:10B2AC000B4B984211D1224628460A4901F0BAF8B4 +:10B2BC0022460121DB20FFF715FB2246012140F23B +:10B2CC000D10FFF70FFB002038BD6FF00400FBE7FB +:10B2DC000000AD0B69B400002DE9F04340F20635D7 +:10B2EC004668D00648BF0320337B02EA050558BFE9 +:10B2FC00C2F3401087B001F01F07B5F5837F1446E9 +:10B30C0047EA43178DF8030046D00FD8062D45D0D9 +:10B31C0007D8B5B1022D14D06FF0150007B0BDE8F9 +:10B32C00F083B5F5807FF7D101250AE040F20222C7 +:10B33C00954235D0B5F5407F34D0B5F5007FEBD1D3 +:10B34C0004250123220303FA01F12DD573685960FA +:10B35C00326972BB5F2F07F01F034FEA571175D884 +:10B36C003C4A52F8212084F4803102EB8302D2F85B +:10B37C008030C1F30041C4F3404423F4706344EAC9 +:10B38C00410423F00F031C4344EA800444EA0524DF +:10B39C00C2F880400020C1E70925D2E70825D0E794 +:10B3AC000625CEE70525CCE7630344BF7368996097 +:10B3BC00CEE71036394630460DF1020201F040F866 +:10B3CC0014F4403F80460CD13946304603F003F864 +:10B3DC00214B9845DED130469DF8021001F09AF8C9 +:10B3EC00D8E74FF0000901AB3946304603AACDE946 +:10B3FC000439CDE90199CDF80C9000F089FE164B7B +:10B40C00984289D114F4003315D09DF8033084F49C +:10B41C008034C4F300448DF80E3039464B46304628 +:10B42C0003AA8DF80C508DF80D4000F067FF0A4B05 +:10B43C009842CDD070E7CDE9043339460DF10303C2 +:10B44C00304603AA039300F063FEF0E7203352F872 +:10B45C002330FFDE48F100000000AD0B4209022A48 +:10B46C00034670B503D0012A18D0CAB170BD0E4E78 +:10B47C00326951680029F9D0012503F01F030C68CB +:10B48C009D400029F2D08A682A4002D030464B6891 +:10B49C0098473CB1236821461C46F2E7034EE7E788 +:10B4AC00034EE5E72346F6E7ECE70000D8E700009B +:10B4BC00C4E70000F0B5406801F01F04057B87B0BD +:10B4CC0044EA45140569002D63D0B2F5001F00F164 +:10B4DC00100506D12146284602F07FFF002007B058 +:10B4EC00F0BD0027B2F5800F01AE019777603BD11C +:10B4FC00B3F1007F14BF052304238DF8043000231F +:10B50C00CDE904630393836823FA01F111F001017F +:10B51C001CD1B2F1A07F19D104F01F035F2C4FEAAC +:10B52C00541003F120033AD81E4A52F8202052F846 +:10B53C002330DB070AD40DF1030632462146284698 +:10B54C0000F07EFF184B984219D0029621462846EF +:10B55C0003AA00F0DDFD154B98421DD101222146B6 +:10B56C00284600F0EFFFB9E7B3F1C06F05D0B3F197 +:10B57C00007F14BF01230223BFE70323BDE731463D +:10B58C00284600F0D3FF094B9842DED06FF00B0039 +:10B59C00A5E76FF08500A2E76FF015009FE751F863 +:10B5AC002330FFDE48F100000400AD0B0000AD0BB2 +:10B5BC00036803F01F025F2B02604FEA53129ABF1D +:10B5CC00024B53F822000020704700BFC0F100006E +:10B5DC002DE9F04F1546064640F2FF18DFF83C9176 +:10B5EC00DFF83CA185B000EB8101B14203D1002012 +:10B5FC0005B0BDE8F08F3368C3F3C322082AC3F348 +:10B60C00080400F284801B0E444508BF4FF0FF3441 +:10B61C0019F80270192B7DD8DFE803F00D1C476078 +:10B62C004A50607C7C7C7C7C7C7C7C7C7C7C7C7CC8 +:10B63C007C7C636D7073C5F80446621C3CD04FF083 +:10B64C00010B03A80394FFF7B3FF039B0BFA03F35F +:10B65C00436005E0C5F80C46601C2DD04FF0000B84 +:10B66C005A463368039403F40040002818BF0022A4 +:10B67C0003A818BF4FF0010B01930092FFF798FF3E +:10B68C00039C019B00EB8400D0F88040C3F3412C59 +:10B69C00009A3B0243EA8C03134304EA0A04234353 +:10B6AC0043EA4B03C0F8803006E0C5F81046C4E707 +:10B6BC00631CC5F8004627D1043696E7601CC5F814 +:10B6CC000446F9D04FF0010B03A80394FFF770FF69 +:10B6DC000122039B02FA03F38360C2E7C5F8084614 +:10B6EC00BAE7C5F86045621C3368E5D0C3F3004384 +:10B6FC00002BE7D0A3E7C5F86445F4E7C5F8684527 +:10B70C00F1E7C5F86C45EEE74FF0000BDCE76FF0A6 +:10B71C0015006DE76FF085006AE700BF16F30000B7 +:10B72C00F0F0FCFF2DE9F04104460D46476802F0AD +:10B73C0099FEA0B9274B1B788BB14FF020086426DB +:10B74C0063681B68D3F830318BBB012002F07CFDA1 +:10B75C00013EF5D10021202001F07AFDEFE72021F8 +:10B76C00EFF311831A4681F31288BFF36F8F606871 +:10B77C000068D0F8300120B983F31188BFF36F8FC4 +:10B78C00EEE73B6A00201D706368396A1B68C3F8DA +:10B79C003C170121C3F84017C3F83001D3F830012E +:10B7AC00196582F31188BFF36F8FBDE8F081EFF359 +:10B7BC0011831A4688F31288BFF36F8F616809688A +:10B7CC00D1F830110029DCD183F31188BFF36F8FCE +:10B7DC00C0E700BF0B04002073B514461D46684635 +:10B7EC0000F078FF009BA342019BAB4114D30B4AA2 +:10B7FC00D2E90061A41B24F01F0365EB010064096E +:10B80C009B1941EB000144EAC060C2E9003102B06F +:10B81C00BDE8704001F0A2BE02B070BD680100200E +:10B82C0010B500220121E420FFF75CF8002000F0A5 +:10B83C007FFF114B0446984208D101200F4901F0BB +:10B84C000DF8A04205D00E4B984211D04FF0FF30AE +:10B85C0010BD67220B4B02201A60FFF771FC0A4ADD +:10B86C000020D2F8183723F44033C2F81837EFE72A +:10B87C006FF00B00ECE700BF0000AD0B1C000020CC +:10B88C000200AD0BCC03002000200E5007B568461B +:10B89C0000F020FF0098074AD2E90032C01A019B41 +:10B8AC004FEA501063EB020340EAC36003B05DF84B +:10B8BC0004FB00BF680100207FB5431C18BF20EAC1 +:10B8CC00E07502A808BF6FF0004500F003FF029975 +:10B8DC000E4B2020D3E900268C1A039961EB06034A +:10B8EC002146C5FB0013402973F1000436BF014605 +:10B8FC0021F01F01002352184FF0010146EB030306 +:10B90C000091034801F00EF804B070BD68010020EE +:10B91C00140000202DE9F043224885B002F088FC89 +:10B92C00002604461F4B204FD3F83880DFF87C905C +:10B93C00B04503D8002005B0BDE8F083636A97E8F2 +:10B94C0007001D6801AB83E807000C221946284646 +:10B95C0002F06EFE78B975B122692B699A420AD150 +:10B96C00AB69A2699A4206D8E2699A4203D9636923 +:10B97C006A6993430CD0256A75B10D4802F0BDF984 +:10B98C00002383F31188BFF36F8F0423184602DF63 +:10B99C00E36801361C44CBE7484602F0AEF9636A13 +:10B9AC001D60F5E7009E000090F0000053F30000CE +:10B9BC002AF3000003689A070CD50368DB070DD542 +:10B9CC000922074BC3F80C250F22C3F804250122CA +:10B9DC001A60FEE7034B0360EF3B0360704700BF48 +:10B9EC0000801050F000FA50064BD3F80438012BAD +:10B9FC0005D0032B02D1044B044A1A607047024B4A +:10BA0C00034AFAE700001250200000200090D003F7 +:10BA1C000020A107012208B5214B2248C3F80028B9 +:10BA2C00FFF7C8FF0830FFF7C5FF0830FFF7C2FF6C +:10BA3C000830FFF7BFFF00F5F470FFF7BBFF1A4AA1 +:10BA4C00D2F88C3043F44063C2F88C300022D3001F +:10BA5C0003F57F0303F54043D3F8001401310AD0FA +:10BA6C00D3F8001439B1D3F800140132D3F80434EC +:10BA7C00402A0B60EBD10A4BD3F840245AB10A4A46 +:10BA8C00D2F88C3043F44063C2F88C300022074B60 +:10BA9C00C3F8A02508BDC822C3F84024EFE700BFB7 +:10BAAC000000125000C50D5000ED00E000B0045035 +:10BABC0030B50468ACB1B4FA84F30122C3F11F03AE +:10BACC00DBB29A4024EA0202D0E8EF5FA54204D12F +:10BADC00C0E8EC2FBCF1000FF6D1EAD102480B7094 +:10BAEC0030BD0248FCE700BF0000AD0B0200AD0BFF +:10BAFC0010B50368CB40DB0710D401238B400268E0 +:10BB0C0043EA0201D0E8EF4F944204D1C0E8EC1FA5 +:10BB1C00BCF1000FF6D1F2D1014810BD0148FCE791 +:10BB2C000000AD0B0400AD0BF7B5044618B10128AD +:10BB3C0022D003B0F0BD0422284B0646C3F80823DC +:10BB4C0001225A61C3F80801D3F80831002342F2EC +:10BB5C001075224F8DF80730D4B1012CE9D1D7F8EC +:10BB6C000C3403F480338EBB002BE2D0012002F0A6 +:10BB7C006BFB013DF3D1DCE70022184B0DF10706FE +:10BB8C00C3F808035860C3F80021D3F80031DDE78F +:10BB9C00D7F84C3403F480333EB9002BC9D00120C4 +:10BBAC0002F052FB013DF3D1C3E7D7F84C2402F06D +:10BBBC0003023270002BBCD09DF80730002BB8D19B +:10BBCC00012002F041FB013DC6D1B2E7002B0CBFB6 +:10BBDC00012300233370EFD1ABE700BF00E010501E +:10BBEC00044B1A7922B9012218601A710248704765 +:10BBFC0002487047D00300200000AD0B0C00AD0BC9 +:10BC0C0008B5084B1B6843B14FF48770FEF75CFE18 +:10BC1C0018B94FF48770FEF749FE0022024BC3F8A7 +:10BC2C00402408BDD003002000E0105010B510B126 +:10BC3C00012844D010BD274BD3F84C14D3F84C2416 +:10BC4C00C80323D502F00303012B04D013B1002049 +:10BC5C00FFF76AFF00231F4A0420C2F84034102269 +:10BC6C004FF48473002103F1A04303F5871319608B +:10BC7C001C68012402F1A04202F587121460164AD6 +:10BC8C00126802B3134BC3F80403D3E7D3F8482468 +:10BC9C00D107DFD5D3F850249207D8D40E4A1268B6 +:10BCAC001AB10422C3F80423C4E7D3F80821002AEC +:10BCBC00FBD00022C3F80821D3F80831BAE70022E0 +:10BCCC004FF48073CEE71A68002AFCD019601B6809 +:10BCDC00B0E700BF00E01050D003002010B51E4BA1 +:10BCEC00D3F8002152B10020C3F80001D3F8002191 +:10BCFC000122C3F80823194B1B689847164BD3F83D +:10BD0C00082172B10022C3F80821D3F80821D3F816 +:10BD1C004C24D3F84C24920714D10122C3F84024AC +:10BD2C001A610D4BD3F80421A2B100220220C3F8F2 +:10BD3C000421D3F80421C3F80803084BBDE81040D4 +:10BD4C001B6818470422C3F80823044B01201B6806 +:10BD5C009847E6E710BD00BF00E01050D00300206C +:10BD6C00036803F01F025F2B02604FEA53129ABF65 +:10BD7C00024B53F822000020704700BF24F2000051 +:10BD8C00034A4309D35C00F01F001844C0B270474B +:10BD9C0086F3000070B505460846FFF7F1FF362123 +:10BDAC002A79174B01FB0202083233F81200C10545 +:10BDBC0025D56C240026C0F3432120F4F85023F839 +:10BDCC0012002A7904FB02340E3434F8020F10F4FA +:10BDDC00807F03D0C0F34320814210D00136232E44 +:10BDEC00F3D16C200024424302EBC1029C502A790F +:10BDFC00BDE8704000FB02306030FFF779BE70BDCB +:10BE0C0024000020F0B50646002487B0114F0191A4 +:10BE1C0017F8012B06AB03EBD20313F8143C02F01A +:10BE2C000702D34013F0010306D10134032CEFD1E8 +:10BE3C000023184607B0F0BD074A03AD07CA85E8D2 +:10BE4C00070055F824100A6A46F824200A62002AD2 +:10BE5C00EBD0EEE796F30000A8F0000010B5044616 +:10BE6C000846FFF78DFF362303FB0400024B083016 +:10BE7C0033F81000400B10BD2400002010B5044610 +:10BE8C000846FFF77DFF362303FB0400034B083005 +:10BE9C0033F81000C0F3400010BD00BF2400002098 +:10BEAC0010B504460846FFF76BFF362303FB04006E +:10BEBC00034B083033F81000C0F3401010BD00BF26 +:10BECC00240000202DE9F04F0D46D0F8141387B054 +:10BEDC00C9B241F40031AA6D009002F030FA81032E +:10BEEC0006462ED50023696E03A8CDE903330593CE +:10BEFC00FFF788FFDFF868A1DFF868B10DF10C09D6 +:10BF0C001AF8013B05EBD30292F8642003F0070307 +:10BF1C0022FA03F3DA076AD4534B09F104095345A7 +:10BF2C000BF1010BECD14FF40031AA6D009802F02B +:10BF3C0006FA696E03A8FFF765FF0028DAD126F42C +:10BF4C0000360124002E73D107B0BDE8F08F91FAB2 +:10BF5C00A1F1B1FA81F19BF8004041EA441420466A +:10BF6C00FFF70EFF083035F81080C8084FEA980329 +:10BF7C000193012319F8002001F0070103FA01F1E4 +:10BF8C0022EA010209F8002002A80294FFF7E8FE59 +:10BF9C00029A019B203250F82220C8F382075B07DB +:10BFAC00B846C2F3014227D52846D7B221464246AD +:10BFBC0002F0A6F902A80294FFF7D2FE029B2033EE +:10BFCC0050F82330C3F301439F4207D10021204690 +:10BFDC0002F087F93946204602F083F902A8029450 +:10BFEC00FFF7BEFE0122029B02FA03F30362D9F8AB +:10BFFC0000100029ABD18FE7022A0DD10321204676 +:10C00C0002F06FF907F00503012BE7D142462146F8 +:10C01C00284602F075F9E1E702212046019202F070 +:10C02C0060F9032FF2D0019A032AD7D1022FECE743 +:10C03C0096FAA6F3B3FA83F304FA03F226EA02069D +:10C04C00009A284602EB8303D3F81015D3F8102579 +:10C05C00C1F30811C2F3014202F052F972E700BFBA +:10C06C0096F3000096F3000099F300002DE9F743D6 +:10C07C0005460846FFF784FE36222B79204F02FB3B +:10C08C000303083337F813300E46DB07044635D567 +:10C09C00284602F07BF995F8049031464846FFF7A4 +:10C0AC00FFFE60B14846FFF7D9FE0022D5F80080AC +:10C0BC0000B208EB8003C3F81025C3F810252846FE +:10C0CC003146FFF767FE36222B7901A802FB0344A9 +:10C0DC000023083427F814300196FFF741FE019B2A +:10C0EC0000EB8300D0F88020064B134043F0020392 +:10C0FC00C0F88030044803B0BDE8F0830348FAE789 +:10C10C0024000020F0F0FCFF0000AD0B0400AD0B90 +:10C11C002DE9FF47174604460846FFF731FE3E68F7 +:10C12C008846054676B394F804904846FFF7A6FE79 +:10C13C0018B14846FFF7B4FEA0BB00234046CDE93A +:10C14C0000338DF80E308DF80F300DF10F023346A1 +:10C15C000DF10E0102F07CF836202379644A00FBC5 +:10C16C000353083332F8131021F0020122F8131094 +:10C17C00237900FB0353083332F8131041F001010B +:10C18C0022F813107B68002B45D0267941463046A7 +:10C19C0093F80090D3F804A0FFF770FE20B1BAF129 +:10C1AC00000F22D0534839E04FF0360C0CFB0656EA +:10C1BC004F49083631F8163023F4604323F020033E +:10C1CC0021F81630BAF1000F0FD0B9F1030FE9D8EE +:10C1DC007B6826685B68187806EB8006B9F1000F5F +:10C1EC001FD1C6F81095C6F8109536202379404A11 +:10C1FC0000FB0353083332F8131021F01C0122F812 +:10C20C001310237900FB0353083332F8131041EA5F +:10C21C00890122F81310BB6863BB374804B0BDE832 +:10C22C00F087D6F8102522F00302C6F810252268F4 +:10C23C0002EB8003D3F8102522F4473222F47C72EF +:10C24C00C3F8102541F6F072D3F8106502EA081213 +:10C25C0042EA09423243C3F8102523790CFB0353FD +:10C26C00083331F8132042EA403242F0200221F820 +:10C27C001320BAE74146D3E900972046FFF78AFD21 +:10C28C00B9F1000FC9D06C232079194E4343F21831 +:10C29C00F358994504D153689F4201D100210CE019 +:10C2AC006C2303FB00600DF10F016030FFF700FC05 +:10C2BC00114B9842B2D19DF80F106C222379534345 +:10C2CC0003EBC10346F8039023795343362203EB67 +:10C2DC00C10333445F60237902FB0353083336F800 +:10C2EC00132042EA412242F4807226F8132094E78C +:10C2FC00240000200400AD0B0000AD0B2DE9FF471E +:10C30C000546084617461E46FFF73AFD8946044681 +:10C31C0087B395F804804046FFF7B0FD18B9404646 +:10C32C00FFF7BEFD68BB362303FB0848DFF8E4A02B +:10C33C0008F108083AF8183013F01C0F02D07B787B +:10C34C00012B1ED001238DF80F3000237A1CCDE970 +:10C35C0000734846BB1C0DF10F0101F079FF36222A +:10C36C002B7902FB034308333AF8132042F0030203 +:10C37C002AF81320002E4BD049462879FFF77EFD72 +:10C38C0018B9254804B0BDE8F08700214FF0360CF1 +:10C39C0030782B68820003EB8003C3F81015C3F8C8 +:10C3AC0010152B791B490CFB0343083331F8137020 +:10C3BC0027F4604727F0200721F8137077783FB3F4 +:10C3CC002B68B6781A44D2F810E5144B3F040EEAE9 +:10C3DC000303C2F8103541F6F07307F4403703EA53 +:10C3EC0009133605D2F810E53B4306F480163343A7 +:10C3FC0043EA0E03C2F810352B790CFB03440834C6 +:10C40C0031F8143043EA403343F0200321F8143060 +:10C41C000348B7E7240000200400AD0B0FE0ECFF4D +:10C42C000000AD0B30B56C250479044B05FB0434CE +:10C43C00A160017905FB0133DA6030BD24000020D6 +:10C44C0070B5067914463046FFF72AFD68B10846E8 +:10C45C00FFF796FC362303FB0600054B083033F838 +:10C46C00103004485B0B237070BD0348FCE700BF21 +:10C47C00240000200000AD0B0400AD0B2DE9F041B1 +:10C48C00064607796C20194D00FB075898F8684050 +:10C49C004CBB404621464622103002F048F921465A +:10C4AC003068D8F858608E4213D10023C0F844315C +:10C4BC00D0F84431C0F308300130FEF7F7F96C23A3 +:10C4CC0003FB075501230A4885F868302B66BDE845 +:10C4DC00F081CBB29B0003F58072442B04BF8450D7 +:10C4EC0083580131DFE70348F1E700BF2400002047 +:10C4FC000000AD0B0C00AD0B6C210279044B01FB61 +:10C50C00023393F86800003818BF0120704700BF51 +:10C51C00240000206C235C22007913FB00230248CA +:10C52C001844FFF7E5BA00BF240000206C235C22FE +:10C53C00007913FB002302481844FFF7B9BA00BF77 +:10C54C00240000202DE9F34103681746D3F8142387 +:10C55C00044692035CBF4FF40032C3F814230679EF +:10C56C000D463046FFF79CFC08B33046FFF786FCBF +:10C57C008046E0B93046FFF771FC05B22168AD008A +:10C58C0005F5807241F802808A5821682944D1F857 +:10C59C00102542F00102C1F810252FB101222168AB +:10C5AC0002FA00F3C1F8143302B0BDE8F08128465A +:10C5BC00FFF7E6FB362303FB06000F4B083033F87E +:10C5CC001030C3F38203042B11D0052B11D001A81A +:10C5DC000195FFF7C5FB019AC368D34003F0010333 +:10C5EC00991C284602B0BDE8F04101F07ABE032147 +:10C5FC00F7E70221F5E700BF2400002001490248BB +:10C60C00FFF760BC2400002000A00D500149024837 +:10C61C00FFF758BC9000002000C0105070B50023EC +:10C62C0067240126027A94FAA4F1B1FA81F19142BD +:10C63C0003F1010518D100210C24120102F1A042D2 +:10C64C0002F56222C2F82C150A4ADBB204FB032362 +:10C65C000168D960416819610121007A18759368E5 +:10C66C0081400B43936070BD06FA01F12B4624EA1E +:10C67C000104D8E7FC000020074B027AD3F8101510 +:10C68C0021F001010A43C3F810250268C3F8A4265F +:10C69C004268C3F8AC26704700200E50012303FA01 +:10C6AC0000F010F0670F0AD0064B5B68DB4303F019 +:10C6BC0067030342044B054818BF184670470448EB +:10C6CC00704700BFFC0000200000AD0B0400AD0B58 +:10C6DC000800AD0B00230022F0B5C0E90023104B7D +:10C6EC00D3F810359B0719D5EFF31181202383F371 +:10C6FC001288BFF36F8F4FF0FF356FF04044094E37 +:10C70C00D6E90023954274EB0307F9D3C0E9002363 +:10C71C0081F31188BFF36F8F0348F0BD0348FCE72A +:10C72C0000200E5040270E500000AD0B0100AD0B49 +:10C73C00F0B53F4C85B02578002D75D13D4A01AB45 +:10C74C0007CA83E807003C491846D1F8102522F0A7 +:10C75C000202C1F81025FFF78FFF0723672202F0B2 +:10C76C0001012944013BCDB24FEA5202F7D1052D0C +:10C77C005CD11A4601272E48002567265FFA82FCF9 +:10C78C0096FAA6F15FFA85FEE645B1FA81F145D33A +:10C79C000132052A017500F10C00EDD101200025B4 +:10C7AC0026499A0800FA02F212F0670F1CBF5D507E +:10C7BC005A580433302BF4D1E420FEF777F81E4B93 +:10C7CC001F49D3F8182703F5C07322F4403242F402 +:10C7DC008032C3F89825D3F8902322F00202C3F8D4 +:10C7EC00902340F6FF72C3F8A82100221A60A3F52B +:10C7FC00807004338B420260F8D101250E4B114A34 +:10C80C001D66114B5D201B68B3FBF2F358430F4BB5 +:10C81C002B4398470E48257005B0F0BD07FA01F17F +:10C82C0026EA01060135ABE70A48F5E70A48F3E7C3 +:10C83C00FC000020B4F0000000200E5000210E502F +:10C84C00B0210E5040420F0020000020D0F000001C +:10C85C000000AD0B0C00AD0B0100AD0B73B52A4CF9 +:10C86C00054663680E4603F0010304F1140101A8A8 +:10C87C000193FFF71DF9254B98423DD100233370EE +:10C88C00636823F001036360A36843F00103A360B2 +:10C89C001F4BD3F81025960732D4D3F8102542F04D +:10C8AC000202C3F81025C5B1D3F8482712F00102D3 +:10C8BC0002BFD3F8481741F00101C3F84817144BD5 +:10C8CC00D3F84017D3F844174C00F9D42AB9D3F84D +:10C8DC00482722F00102C3F848270E4B0E4A1B686A +:10C8EC00B3FBF2F30A4A03EB43135BB1D2F84017E4 +:10C8FC00D2F84417490001D402B070BD013BF4E7F3 +:10C90C000648F9E70648F7E7FC0000200000AD0BED +:10C91C0000200E502000002040420F000C00AD0BF8 +:10C92C000700AD0B2DE9F0410446007A1D46FFF7D8 +:10C93C00B5FE1A4B1746984206469DF8188029D129 +:10C94C002046FFF76BFEEFF31180202383F3128850 +:10C95C00BFF36F8F227A124B9200134400221A609D +:10C96C00217AC5F31305090101F1A04101F56221FA +:10C97C001B68C1F82075C1F8245580F31188BFF3EA +:10C98C006F8FB8F1000F05D00123227A9340054A2E +:10C99C00C2F824333046BDE8F08100BF0000AD0B77 +:10C9AC0000210E5000200E50F7B5254A0020536888 +:10C9BC009268DB4303F0670313401C4601260746CD +:10C9CC000CBB01271F4B1E4DD3F820630640287D5E +:10C9DC0007FA00F333420FD0E96869B10201D5F8C8 +:10C9EC0010C002F1A04202F56222D2F82435D2F82E +:10C9FC002025CDF800C088470134052C05F10C0525 +:10CA0C00E5D103B0F0BD94FAA4F5B5FA85F5AA000A +:10CA1C0002F58071920806FA02F213420BD001F172 +:10CA2C00A04101F56221D1F800C0BCF1000F02D089 +:10CA3C000F600968104306FA05F524EA0504BFE700 +:10CA4C00FC00002000200E5038B5EFF31185202398 +:10CA5C0083F31288BFF36F8F064B9C6801F0D7FCF1 +:10CA6C0085F31188BFF36F8F2046BDE83840FDF782 +:10CA7C009FBE00BFD803002038B5094B53F82040A7 +:10CA8C00013053F82050A54200D838BD636823B15B +:10CA9C00204601F0BFFC0834F5E723689847FAE715 +:10CAAC0030F20000012270B52A4B032086B01A70B8 +:10CABC00FFF7E2FFFDF782FC01F0C8FC0420FFF752 +:10CACC00DBFF254C254D2646AE4204F1300410D335 +:10CADC0000F000FB204C224EAC4226D300F00EFBA3 +:10CAEC00FDF70EFB1F4A137B23F00103137306B0F3 +:10CAFC0070BD54F80C3C059354F8103C049354F856 +:10CB0C00143C039354F8183C029354F81C3C0193C6 +:10CB1C0054F8203C009354E90A2354E90C0100F02A +:10CB2C0019F954F8303C5E65CDE7D4E90A23B3F12A +:10CB3C00FF3F08BFB2F1FF3F05D052EA0301206866 +:10CB4C0003D101F095FC3034C6E73146183000F0C3 +:10CB5C008BFCF8E70B0400208CF000008CF000003C +:10CB6C0089E50000F001002008B50348034A0021C4 +:10CB7C00121A01F067FC08BD6801002010040020A7 +:10CB8C000023012270B586B0CDE904230F22019356 +:10CB9C00CDE9023214234FF4A0720E4D0E4E03FB5E +:10CBAC0000550E4906EBC016044602FB00110C4B57 +:10CBBC003046009500F0CEF8737B2C7423F0040300 +:10CBCC007373084B013403EBC423EE606B6006B047 +:10CBDC0070BD00BFD803002070010020100C002095 +:10CBEC005BE4000010040020BFB50020FFF744FFF9 +:10CBFC002B4B03F5006282F3088883F30A88002428 +:10CC0C00E023284D284E85F82230EC772C766C7674 +:10CC1C00AC766B6A254F43F4E0236B6285F82340B6 +:10CC2C00FDF744FDFDF7D0FE4FF0FF33AB62EB6236 +:10CC3C00FEF75AF8FDF7F0FE40F201121C4B1D4DA9 +:10CC4C009A81B360C3E91844DC6601F0CCFB012087 +:10CC5C00FFF712FF0220FFF70FFF00F06FFA164BE1 +:10CC6C0075610593012315494FF48062CDE90343A7 +:10CC7C0028463B46CDE90144009400F06BF806468B +:10CC8C006A7B284622F004026A7301F02DFC2046D0 +:10CC9C00FFF776FF3A4631462846FDF771FE00BF96 +:10CCAC001004002000ED00E0D8030020B1CA000001 +:10CCBC0070020020F001002099F30000500D0020BC +:10CCCC0038B50446EFF31185202383F31288BFF3A4 +:10CCDC006F8F01F057FC48B10022826701F004FC11 +:10CCEC002946BDE83840074800F0B0B9D4E9023213 +:10CCFC009A4218BF0133A36085F31188BFF36F8F7D +:10CD0C0038BD00BF0C04002013B5EFF311812024B3 +:10CD1C0084F31288BFF36F8F846844B1013C846044 +:10CD2C0081F31188BFF36F8F002002B010BD52EA5F +:10CD3C00030406D181F31188BFF36F8F6FF00F00DE +:10CD4C00F3E7CDE900230246014800F053F9ECE784 +:10CD5C000C0400202DE9F043984685B000F15803EF +:10CD6C00C0E91633109B002603730423073222F00C +:10CD7C00070543730F9B01EB05090F468373494667 +:10CD8C00C0E906668660C6730446FDF729FB0E9B58 +:10CD9C00073020F00700A066C4E9187502930D9BBC +:10CDAC00A9EB000901930C9B4A4600933946434674 +:10CDBC002046FDF7C9FD054B66659B6803B1DB6E2C +:10CDCC004846E36605B0BDE8F08300BFD8030020F9 +:10CDDC0038B50D4D2B4653F8184F9C4200D004B972 +:10CDEC00EC68AB6838B95A7BD20604D1A26912B987 +:10CDFC00DA897F2A05D89C4202D0204600F07CFAC2 +:10CE0C0023466B6138BD00BFD803002008B590F9EC +:10CE1C000D200146002A437B04DA03F07F034373A1 +:10CE2C0001F028FB044B9868431A58425841BDE85E +:10CE3C000840FFF7CDBF00BFD803002070B490F9B5 +:10CE4C000D30427B002B29DBD30627D183692BBB0A +:10CE5C00134962F07F0242730A4652F8184F94420B +:10CE6C0018BF2346CC6923B9C0E900242060C861EF +:10CE7C000CE090F90E5093F90E60B5420AD0AE4218 +:10CE8C0008DD5A68C0E9003210605860002070BCA0 +:10CE9C00FFF79EBF9C42E7D01B68E4E770BC70476D +:10CEAC00D8030020002A2DE9F0410E460CBF1021BA +:10CEBC000821427B044611420FD141EA0203D209F8 +:10CECC0018D1437308291BD00120FFF781FFBFF352 +:10CEDC005F8F637B23F060036373274B9B68A342D4 +:10CEEC0043D1EFF30583002B3FD13046BDE8F04131 +:10CEFC00FDF774BC03F07F03437301F0BBFAE1E769 +:10CF0C00A36813B1204601F0BDFA04F1180001F03A +:10CF1C0076FBA56D04F15807AF4211D04FF0000815 +:10CF2C0075B1284601F0AEFA05F1180001F067FB67 +:10CF3C002846C5F87880FFF781FFA56DAF42EFD189 +:10CF4C000D4DAB689C42BFD1EFF30583002BBBD0DA +:10CF5C000120FFF73DFF40F20112084B9A8100229D +:10CF6C00AB60C3E91822DA66B1E786F31188BFF328 +:10CF7C006F8FBDE8F08100BFD80300207002002045 +:10CF8C0070B590F90D200146002A437B04DA03F0BA +:10CF9C007F03437301F06EFA154A4B7B104663F026 +:10CFAC007F034B7350F8183FD469834208BF0023AA +:10CFBC0023B9C1E900042160D1610CE091F90E5054 +:10CFCC0093F90E60B5420ED0AE420CDD5868C1E943 +:10CFDC000030016059609068BDE87040431A5842B7 +:10CFEC005841FFF7F5BE9C42E3D01B68E0E700BF59 +:10CFFC00D8030020F8B5124B0C46DDE90676114635 +:10D00C009868104A1060EFF31182202282F3128884 +:10D01C00BFF36F8F9D68284601F03EFAB6F1FF3FD3 +:10D02C0008BFB7F1FF3F06D03A463346064905F133 +:10D03C00180000F019FA2046BDE8F840FDF7CEBB09 +:10D04C00D8030020F803002089E5000049B9EFF36C +:10D05C00058030B9054B5A699B689A4201D0FDF79F +:10D06C00BDBB81F31188BFF36F8F7047D8030020CD +:10D07C0010B5EFF31184202282F31288BFF36F8F67 +:10D08C00427B510704D484F31188BFF36F8F10BD1A +:10D09C0022F004024273FFF7D1FE2146BDE8104096 +:10D0AC000148FFF7D3BF00BF0C040020034648B96A +:10D0BC00EFF3058232B9064A51699268914201D068 +:10D0CC00FDF78CBB83F31188BFF36F8F704700BFE4 +:10D0DC00D8030020EFF31181202383F31288BFF3D0 +:10D0EC006F8F054B9A68D37B013BD37381F3118807 +:10D0FC00BFF36F8F704700BFD803002038B5EFF334 +:10D10C001185202484F31288BFF36F8F0A4B002003 +:10D11C009A68D37B0133D373FFF75AFE85F31188DA +:10D12C00BFF36F8FEFF3118084F31288BFF36F8F0F +:10D13C00BDE83840FFF7BABFD8030020024B03F11B +:10D14C001802C3E906227047D8030020F8B5EFF3A4 +:10D15C001185202383F31288BFF36F8F19498868D8 +:10D16C00437B03F07F03437301F084F908468B681B +:10D17C005A7B62F07F025A7350F8182FCC698242A6 +:10D18C0008BF002222B9C3E900042360CB610CE084 +:10D19C0093F90E6092F90E70BE420ED0B7420CDDC0 +:10D1AC005168C3E900210B6053600120FFF710FEAA +:10D1BC002846BDE8F840FDF711BBA242E3D0126847 +:10D1CC00E0E700BFD803002050EA01032DE9F0414D +:10D1DC0005460E4603D1FFF7B9FF00202BE0831C58 +:10D1EC0071F1FF3329DB01F01FFA2C18EFF31188D2 +:10D1FC00202383F31288BFF36F8F124F124BB86842 +:10D20C001860FFF703FEB86810492A4633461830F9 +:10D21C0000F02AF9BA684046537B43F0100353736D +:10D22C00FDF7DCFA01F000FA201A63EB0303012886 +:10D23C0073F10003D1DBBDE8F0816FF00104241A17 +:10D24C00D4E700BFD8030020F803002089E50000D4 +:10D25C00B1F1FF3F08BFB0F1FF3F38B506D10B4B22 +:10D26C00986801F050F94FF0FF3038BDFFF7ACFF74 +:10D27C004FF47A750446002147F6112047F6122226 +:10D28C000023C4FB0501FCF7D3FDEEE7D803002017 +:10D29C00014B9868704700BFD80300200122044B53 +:10D2AC00C01A044BC0105843034B1A54704700BFAC +:10D2BC00F0020020ABAAAAAA0C040020C3897F2B81 +:10D2CC0012D80A4B1B6863B1094B90F90E201B68EE +:10D2DC009A4209DB437BDB0606D1064BC31A18BF07 +:10D2EC00012303F0010070470023FAE7000400203B +:10D2FC00FC0300207001002070B50E4B0E4C1E7C00 +:10D30C00054606EB460304EBC304204601F077F90F +:10D31C0000220A4B28469A55FFF7D0FF48B1084B1C +:10D32C002046BDE870401A680649013AD31700F050 +:10D33C009BB870BDD8030020F00200200C04002024 +:10D34C0000040020A9D2000038B5EFF3118520238A +:10D35C0083F31288BFF36F8F104B114A9C681068CF +:10D36C00A04206D1FFF7C8FF85F31188BFF36F8F7A +:10D37C0038BD00211B7C11600A4AD35C002BF3D012 +:10D38C002046FFF79BFF0028EED0637BDB0602D123 +:10D39C002046FFF7F5FD2046E4E700BFD803002048 +:10D3AC00F80300200C04002008B50648064A07497B +:10D3BC00121A01F049F8BDE80840054A054906482B +:10D3CC0001F042B80000002062010020B4F300001C +:10D3DC0000000000B4F3000000000020034B1B68A9 +:10D3EC000BB9FEF753BA0020704700BF04040020AD +:10D3FC0030B570B10B4B5B6898420AD0036843B1EF +:10D40C00D3E90421D0E90445121941EB0501C3E924 +:10D41C000421D0E9003213605A600023C0E90033C4 +:10D42C0030BD00BF5001002010B50E4B1C689C4253 +:10D43C0004D1FFF7D3FF6FF0004010BDFFF7CEFF14 +:10D44C00002CF8D0D4E904321B1A62EBE072B3F171 +:10D45C00004F72F10001EEDA002AACBF1846002032 +:10D46C00EBE700BF50010020B3F1FF3F08BFB2F162 +:10D47C00FF3FF8B504461D4616465ED08160EFF3BB +:10D48C001187202383F31288BFF36F8F013275F15C +:10D49C00FF3324DA6FF001032D49D1E900209B1AE8 +:10D4AC004FF0FF3262EB00009E1B60EB0500012E7B +:10D4BC0070F1000301DA01260020C4E90460254E56 +:10D4CC00D6F800C07568B44508BF4FF0000CBCF12D +:10D4DC00000F0DD1C4E900652C6074601CE0FFF7EF +:10D4EC007DFF013645F10005361845EBE070E4E7A9 +:10D4FC00DCE90420D4E90431934271EB000E1DDA0F +:10D50C00D21ADCF8043060EB0100CCE90420C4E949 +:10D51C0000C31C60CCF804403368B34209D09C4271 +:10D52C0007D10D4B1C6824B9FFF77EFF2146FEF78F +:10D53C00C3F987F31188BFF36F8FF8BD9B1A61EBAA +:10D54C000001AC45C4E90431C4D0DCF800C0BEE72E +:10D55C000803002050010020040400202DE9F84F9E +:10D56C00EFF3118C202383F31288BFF36F8FDFF856 +:10D57C00B0A0DFF8B0900024C9F80000DAF8000081 +:10D58C000025504508BF0020274FE046D7E9002171 +:10D59C00D9F8003078B3D0E9046C4FEAE37EB3429B +:10D5AC007EEB0C0B05DAF61A6CEB0E04C0E9046486 +:10D5BC0021E0B21841EBE671C0E90445C7E900214E +:10D5CC00FFF716FF88F31188BFF36F8F83689847B6 +:10D5DC00EFF31188202383F31288BFF36F8FD9F8F0 +:10D5EC000030DAF800009B1B5045C446C9F80030E7 +:10D5FC00CBD1D7E9002100249A1841EBE373C7E99A +:10D60C000023C9F80040FFF70FFF2146FEF754F93D +:10D61C0088F31188BFF36F8FBDE8F84FFFF794BE06 +:10D62C0050010020040400200803002010B5EFF383 +:10D63C001184202383F31288BFF36F8FFFF7CEFE84 +:10D64C000346054AD2E90001181841EBE37184F353 +:10D65C001188BFF36F8F10BD080300200FB407B5FE +:10D66C0004AA064B52F8041B1868019200F008F843 +:10D67C0003B05DF804EB04B0704700BF0CF1000080 +:10D68C002DE9F04F43688DB01546804602938378A0 +:10D69C009A0740F10D8100240E78002E00F00A81CB +:10D6AC00252E0BD04B1C0393013441463046029B74 +:10D6BC0098470028C0F2F6800399EDE74E788B1C52 +:10D6CC00252E0393F0D00027B946BB46BBF11F0FA4 +:10D6DC0038D8A6F12003102B0AD8DFE803F02A096A +:10D6EC0009300909092109090928092D09091F000F +:10D6FC00A6F13003092B33D92A2E37D11BF0400F5A +:10D70C0055F8043B40F08280002BA7BF4BF0200B58 +:10D71C009946C3F100094BF0280B01E04BF0010BCB +:10D72C00039B13F8016B0393002ECFD130E04BF029 +:10D73C00020B4BF0040BF3E74BF0080BF0E74BF04C +:10D74C00100BEDE7BBF17F0F19D8A6F13003092BB5 +:10D75C00D2D81BF0400F03D00A2101FB0737DFE7BB +:10D76C000A214BF0200B01FB0939D9E72E2E06D1EB +:10D77C001BF0400F40F09E804BF0400BD0E76C2E1E +:10D78C0031D02BD8682E36D06A2E3CD04C2E3AD0C5 +:10D79C00002F46F02003B8BF2BF04002A3F1650127 +:10D7AC00BCBF00271FFA82FB022901D9612B2FD1A4 +:10D7BC0007350727DFF8ACA225F0070508351BF065 +:10D7CC00080F66D00AEB070B56465E4563D1A4EBF7 +:10D7DC000A0AA9EB070906EB0A026FE1742E9FD027 +:10D7EC007A2E9DD0D4E71BF0800F18BF4BF4007B32 +:10D7FC004BF0800B94E71BF4807F18BF4BF4007B3D +:10D80C004BF4807B8CE74BF4207B89E71F4687E742 +:10D81C00A6F16301122900F2F080DFE811F0130089 +:10D82C004F00EE00EE00EE00EE004F00EE00EE00BA +:10D83C00EE00EE00EE00FE00E900EE00EE001B0034 +:10D84C00EE00AF0055F8043B01270DF1180A8DF8D6 +:10D85C001830B4E755F804AB834BBAF1000F08BF8E +:10D86C009A461BF0400F504614BF39464FF0FF311B +:10D87C0000F0EEFE0746A2E741462020029B9847A7 +:10D88C0000280FDB013EB742F6D34C44B146A41B33 +:10D89C0098E74E46F7E7414616F8010B029B98476E +:10D8AC00002892DA98F8023043F0040388F802302A +:10D8BC004FF0FF3420460DB0BDE8F08F1BF0800F09 +:10D8CC0034D01BF4007F2CD0073525F00705AA4671 +:10D8DC0069685AF8080B2BF010050029ADB237DA3D +:10D8EC00404245F4806561EB41010DF1180B0A23B0 +:10D8FC005A4600F0B9FEA0EB0B0301936B0662D500 +:10D90C00019B25F001019F4289B240F3A180002EBA +:10D91C0000F0998015F0100240F09C80BB460D463B +:10D92C0023E0AA465AF8040BC117D4E7AA461BF405 +:10D93C00807F5AF8041B01D10846F5E71BF4007FE1 +:10D94C0019BF48B241F3C01108B241F3C031C2E76C +:10D95C0050EA0103C9D11BF0400FC6D0002FC4D12F +:10D96C002BF01102BB46019795B240F2064115EA25 +:10D97C0001021CBF0BF1010B00222CE02BF010025A +:10D98C000A231FFA82FB1BF0800C49D01BF4007198 +:10D99C0015BF0735AA4625F007055AF8040B1EBF1C +:10D9AC00AA4669685AF8080B50EA010545D12BF0D4 +:10D9BC0016051BF0400FADB242D0002F40D12BF01A +:10D9CC001702019795B215F0100248D0019B002E5A +:10D9DC0042D103F1010B29075AD4EB0706D5D945DF +:10D9EC006FDDA9EB0B030199CB465F185B4649E051 +:10D9FC004BF0100B10237826C5E7782B04D1C6F119 +:10DA0C00780343F01003BEE741462520029B98475C +:10DA1C000028FFF647AF023447E608230026B2E79A +:10DA2C00AA461BF480715AF8040BBDD01BF400718C +:10DA3C001ABFC0B2614680B2B6E72BF0060295B2AF +:10DA4C000DF1180B54E725F01105BB46ADB28CE770 +:10DA5C000D46B8E73B460D4603F1020BBBE7DDF87C +:10DA6C0004B082E7A5F300009EF30000CDE9043278 +:10DA7C0041462020029B98470028FFF613AF049BD9 +:10DA8C00059A01339945F1DCA4EB0B019B46CC18AC +:10DA9C000AB341463020029B98470028FFF602AF9C +:10DAAC008EB901342744391B019B994224DC1D4655 +:10DABC0055BB019AA9EB0B095546224414469144D7 +:10DACC0033E0019F92E7023441463046029B98476F +:10DADC000028E7DAE6E640F206410D42E2D015F006 +:10DAEC00020F04F10104414614BF2B20202015F431 +:10DAFC00806F18BF2D20E9E741463020029B01348E +:10DB0C0098470028CFDACDE606AB013D4146585D7B +:10DB1C00029B98470028CBDAC4E641462020029BA2 +:10DB2C00013498470028FFF6BDAEA9EB0403002B87 +:10DB3C00F3DCC1E57047836800EBC101B3F5004F1E +:10DB4C002CBF51F8220031F81200704700EBC101D4 +:10DB5C008068B0F5004F2CBF41F8223021F812300C +:10DB6C007047012208B5FFF7E6FFD04008BD836877 +:10DB7C0000EBC101B3F5004F08D34B681AB143F069 +:10DB8C0001034B60704723F00103FAE74B881AB18D +:10DB9C0043F001034B80704723F00103FAE7530075 +:10DBAC000122FFF7D3BFB0F5004F34BF0423082385 +:10DBBC00481C0833A0EBD300B0FA80F0C0F11F0072 +:10DBCC0070472DE9F0410D4686680446FFF7C9FF02 +:10DBDC00B6F5004F014601D3012832D03046FFF78D +:10DBEC00E2FF04EB80010E6996B90122E3688240E2 +:10DBFC001343E36002220D612B4629462046FFF7B2 +:10DC0C00A5FF032229462046BDE8F041FFF79EBF41 +:10DC1C00022231462046FFF78EFF0746034629466F +:10DC2C002046FFF793FF0322334629462046FFF791 +:10DC3C008DFF39462B462046FFF788FF02223146DE +:10DC4C00E1E7BDE8F081B2F5802F2CBF0823042357 +:10DC5C002DE9F0410026D21A8C18CD1D25F00705B0 +:10DC6C0024F00704641BE408056021462046AC60E0 +:10DC7C00EE60FFF798FF421D9200013002F107079A +:10DC8C004FEAD7073146A2F1100205F11000B8BFD8 +:10DC9C00324600F04CFD3A4631462846FFF77FFFEE +:10DCAC003346324631462846FFF750FFA4EB0708AF +:10DCBC00284631460122FFF75AFF42463946FFF704 +:10DCCC006EFF3B46324639462846FFF73FFF21465A +:10DCDC002846FFF764FF4346324621462846FFF7A5 +:10DCEC0035FF214628460122FFF741FF3946BDE8A2 +:10DCFC00F041FFF766BF002070470FB407B504A9C9 +:10DD0C0051F8040B0191FCF713FA03B05DF804EB26 +:10DD1C0004B07047038B13F0070305D10368002B85 +:10DD2C000CBF002003207047022B05D1408BB0FAAA +:10DD3C0080F0400980007047012B05D10368002B4F +:10DD4C000CBF0020052070470020704710B50C4612 +:10DD5C0000B111B96FF0150010BD081D00F093F85B +:10DD6C000028F9D1A368032BF6D9F3E72DE9F0418C +:10DD7C000D4607461946281D90461E4600F094F89D +:10DD8C00044638B13346424629463846A446BDE8D7 +:10DD9C00F0416047BDE8F08110B4EFF3118220240C +:10DDAC0084F31288BFF36F8F416110BC0121FCF723 +:10DDBC00F1B938B50C46054658B151B10B6843B1B1 +:10DDCC004B6833B11C22002100F0B1FC00202C6107 +:10DDDC0038BD6FF01500FBE770B505460E46FFF732 +:10DDEC00B5FF041E14DBEFF31182202383F312889A +:10DDFC00BFF36F8F4FF6FF716B8B2C8B8B4204F044 +:10DE0C00070407D182F31188BFF36F8F6FF00A04F8 +:10DE1C00204670BD022C0CD101336B8382F3118828 +:10DE2C00BFF36F8F0023224631462846FFF79EFF33 +:10DE3C00EEE7062C14D8DFE804F004131313041ACD +:10DE4C000400002333606B683BB9C5E90066ACB9CC +:10DE5C0002212846FCF79EF9DAE71E606E60F6E7B1 +:10DE6C0082F31188BFF36F8F6FF00404D0E782F355 +:10DE7C001188BFF36F8F6FF08504C9E782F31188A7 +:10DE8C00BFF36F8FC4E7034658B1426802F0030238 +:10DE9C00012A03D0032A04D1026812B10020986031 +:10DEAC0070476FF0150070474268034602F003029A +:10DEBC00032A4FF00002816014BF104600685A60BC +:10DECC0070477047012801D1EFF30583FEF7BCBD05 +:10DEDC0001460068FFF7F6BF08B5FCF77DFABDE810 +:10DEEC000840FCF79DBA00210120FFF7EBBF036E41 +:10DEFC0083F30B88704761B662B680F31188BFF369 +:10DF0C006F8F704772B6002383F31188BFF36F8F46 +:10DF1C00BFF34F8F30BF62B6BFF36F8F704772B6CF +:10DF2C00002383F31188BFF36F8FBFF34F8F20BF94 +:10DF3C0080F31188BFF36F8F62B6704740E800F32F +:10DF4C0013F4803FD8B208BF6FF01500704730B59E +:10DF5C000C684B6885B00A894D890094013B24F00C +:10DF6C001F04234402F01F0242EA451223F01F0350 +:10DF7C0069468DF808200393FCF784FD05B030BD8D +:10DF8C0008B5FCF73FFF002008BD0C220369C9B29D +:10DF9C0001FB0233186C00F00700704770B5C9B272 +:10DFAC000369EFF31185202484F31288BFF36F8F7C +:10DFBC000C2606FB0134246C14F0C00408D0A242D9 +:10DFCC0006D085F31188BFF36F8F4FF0FF3070BD13 +:10DFDC00012206FB01331A6485F31188BFF36F8F9E +:10DFEC00436803EBC1035B6898470020EFE780228E +:10DFFC00FFF7D4BFF8B5069F0469C9B2EFF31186D9 +:10E00C00202585F31288BFF36F8F0C2505FB014487 +:10E01C00256C05F0070CBCF1010F0CD1276486F3BD +:10E02C001188BFF36F8FC4E90E23436853F8313066 +:10E03C0098470020F8BD05F0C005AF4286F3118863 +:10E04C00BFF36F8F0CBF6FF077004FF0FF30F1E72D +:10E05C0013B580240094FFF7CDFF02B010BD10B4AF +:10E06C001C240069CBB203FB0400002110BC104738 +:10E07C000020FDF7DBBD002000F0E8B81046FEF7ED +:10E08C001FBE436800205B68DB680B607047436809 +:10E09C0002EA01005B6821EA0201586000209960E5 +:10E0AC007047436800205B685960704743680020E4 +:10E0BC005B689960704743685A68136821EA0300EB +:10E0CC000B40506000209360704710B5036958688E +:10E0DC00F8B1884219D19C680868A142586000D1F7 +:10E0EC009860002008602AB15A680A609A685960E2 +:10E0FC0002B99960002010E0814206D108682060C6 +:10E10C0098688142EDD19C60EBE7044600680028DA +:10E11C00F2D1002AE8D16FF0150010BD30B543687C +:10E12C001360057A446804EBC504A34202D36FF074 +:10E13C00010030BD5C798C4201D00833F0E700203F +:10E14C00F7E743681B68D3F83031704743681B68A6 +:10E15C00D3F88004C3F88004704742681368D3F87E +:10E16C004C0150B10020526A12780A70C3F84C016D +:10E17C00D3F84C2101229A6270474FF0FF30704760 +:10E18C0037B5044600220121C620FCF7ABFBC620A4 +:10E19C00FCF78CFB626853691068C0F82435917EDB +:10E1AC00537ED58B0B43918B127E294389B20B4343 +:10E1BC001343C0F86C3561680B68D3F8002242F445 +:10E1CC008022C3F800220822C3F800250A6A012124 +:10E1DC00C3F83C270022C3F840271965D3F8301147 +:10E1EC000029FBD0C3F8002563680021DD6801AA73 +:10E1FC002846FFF793FF002805DB019B2A68197955 +:10E20C001868FDF7E5F9082162681368C3F8001572 +:10E21C00117A51B9526AC3F804270122C3F80827AE +:10E22C00C3F84C11D3F84C119A62002003B030BDE6 +:10E23C0003465A6B00233C309A4200D17047C168A8 +:10E24C0001330844F8E7004700F0C9B8FDF76CBC8F +:10E25C00F7B51646DDE90847019001A81D46FDF704 +:10E26C007FFD019BB6F1000C18BF4FF0010C00EBC9 +:10E27C0083000B1E18BF0123002D43EA4C0314BF6F +:10E28C004FF00C0C4FF0000C002C43EA0C0314BFA5 +:10E29C004FF4706C4FF0000C002F43EA0C0314BFCA +:10E2AC004FF4403C4FF0000CD0F8802043EA0C03B4 +:10E2BC0022EA030301B109780EB1367876001943CE +:10E2CC000DB12D78AD0031430CB1247824022943D3 +:10E2DC000FB13F783F040C433C43C0F8804003B07F +:10E2EC00F0BD1FB50DF10F03019300238DF80F1036 +:10E2FC001A4619460093FFF7ABFF05B05DF804FB17 +:10E30C0070B5044608461646FDF73AFD083034F859 +:10E31C0010300D46DA0509D5C3F3432304EBC302D1 +:10E32C003146284654F8333052689847A3682BB1CD +:10E33C0031462846E268BDE87040184770BDF0B51C +:10E34C000446002001270546844695420AD1D4F89C +:10E35C00443133B10023C4F84431D4F8443140F48F +:10E36C000030F0BDEBB29B0003F580769B0807FAFA +:10E37C0003F3194208D054F806E0BEF1000F03D0A5 +:10E38C0044F806C0A65918430135DEE770B5067986 +:10E39C00044630460D46FDF783FDB0B13046FDF71F +:10E3AC006DFD90B93046FDF759FD0123226883407D +:10E3BC00C2F81833226803B202EB8303D3F810259A +:10E3CC0022F00302C3F8102570BD2846BDE870404A +:10E3DC000021FFF786BFFDF749BEFFF7D7BF08B195 +:10E3EC00FCF788BD7047704740B1C368587810F08F +:10E3FC0001001EBF1878B0FA80F040097047EFF3A7 +:10E40C001183202383F31288BFF36F8FFEE708B5C7 +:10E41C00FFF7F5FF10B5D0E9003433B90020E268FE +:10E42C00537843F00103537010BD204698470028E1 +:10E43C00F4D00028B8BF4042FF28A8BFFF20E368F3 +:10E44C001870ECE700F073B900F064B97047202441 +:10E45C0008B5EFF3118384F31288BFF36F8FFFF7C6 +:10E46C0051FDF6E7EFF30580003818BF0120704727 +:10E47C00FEF7FEBDD0E9003213605A600023C0E9FC +:10E48C000033704708B5FFF7F5FF437B23F0020319 +:10E49C0043730023836008BD38B504460D46FEF770 +:10E4AC00B5FC637B43F002036373E5B1A5602B6895 +:10E4BC009D4209D16B68C4E900531C606C6012E08A +:10E4CC006A689A42F6D01B68002BF3D094F90E20A0 +:10E4DC0093F90E108A42F3D09142F1DD5A68C4E9E7 +:10E4EC00003214605C6038BD10B5EFF3118420234A +:10E4FC0083F31288BFF36F8FFEF7A0FC84F31188AF +:10E50C00BFF36F8F10BD10B50446183000F077F8CC +:10E51C00EFF31181202383F31288BFF36F8F627B9B +:10E52C0012F0100204D081F31188BFF36F8F10BD6D +:10E53C002046BDE81040FEF7B5BC38B50446EFF3F5 +:10E54C001185202383F31288BFF36F8F437B13F065 +:10E55C00280F0BD183680BB1FFF794FF637B51B984 +:10E56C0003F0FB0320466373FEF768FC85F3118808 +:10E57C00BFF36F8F38BD03F0EB03F3E701211838BD +:10E58C00FFF7DBBF38B5EFF31185202383F3128837 +:10E59C00BFF36F8F0468A0420DD034B12046FFF753 +:10E5AC0071FF04F1180000F02AF885F31188BFF30D +:10E5BC006F8F204638BD0024F7E7EFF3118120223E +:10E5CC0082F31288BFF36F8F027BD2070CD581F3D5 +:10E5DC001188BFF36F8F002383F31188BFF36F8F04 +:10E5EC000423184602DF7047437B1B0704D581F3D5 +:10E5FC001188BFF36F8F70470122FEF753BC10B523 +:10E60C00EFF31184202282F31288BFF36F8F03681B +:10E61C003BB1FEF7EDFE002084F31188BFF36F8F42 +:10E62C0010BD6FF01500F7E708B5FEF7FFFF08BD4A +:10E63C000139024410B5904201D1002005E0037865 +:10E64C0011F8014FA34201D0181B10BD0130F2E7A5 +:10E65C000246014410B58A42134603D01C7801329D +:10E66C00002CF8D1181A10BDF0B5C3F137070446C9 +:10E67C0003F01F031046FFB2082BE6B232D0102B6A +:10E68C003DD06508A40845EAC17544EA81748A083E +:10E69C002D1942EB5102290941EA02716D1842EB26 +:10E6AC001212290A41EA02616D1842EB1222290C5E +:10E6BC0041EA02416D1842EB1242AD1842F10002E0 +:10E6CC00EC0844EA4274D10804EB8402A6EB420540 +:10E6DC00EDB2092D0CD90134A5F10A0541F1000167 +:10E6EC00EDB212E0E40806F0070544EA4174C908EB +:10E6FC00303554EA010200F8015BBDD1F0BD2409AC +:10E70C0006F00F0544EA01740909092D84BFED19BF +:10E71C00EDB2EDE70A44431E914200D1704710B5AB +:10E72C0011F8014B914203F8014FF9D110BD02448D +:10E73C000346934200D1704703F8011BF9E7000030 +:0CE74C0001480068704700001003002026 +:08E7580018B7FF7F010000006B +:10E760005DAE000000000000DDAF0000B0E700007B +:10E7700091B20000D8E7000091B20000C4E70000A9 +:10E7800091B20000ECE700008DE1000000E800001D +:10E7900041B20000000000002DB8000000000000A1 +:10E7A00039AE00000000000021B9000000000000A8 +:10E7B000E7F200001CF100002CF1000058010020DD +:10E7C0004C030020F4F2000054F100009CF1000022 +:10E7D0005A010020A003002000F300006CF10000AB +:10E7E0009CF100005C010020AC0300200BF3000052 +:10E7F00084F100009CF100005E010020B8030020BD +:10E800001FF30000CCF1000018F2000060010020AE +:04E81000C40300201D +:10E8140000000000F3DE000000000000F3DE000052 +:10E8240000000000F3DE000000000000F3DE000042 +:10E8340000000000F3DE000000000000F3DE000032 +:10E8440000000000F3DE000000000000F3DE000022 +:10E8540000000000F3DE000000000000F3DE000012 +:10E8640000000000F3DE000000000000F3DE000002 +:10E8740000000000F3DE000000000000F3DE0000F2 +:10E8840000000000F3DE000000000000F3DE0000E2 +:10E8940000000000F3DE000000000000F3DE0000D2 +:10E8A40000000000F3DE000000000000F3DE0000C2 +:10E8B40000000000F3DE000000000000F3DE0000B2 +:10E8C40000000000F3DE000000000000F3DE0000A2 +:10E8D40000000000F3DE000000000000F3DE000092 +:10E8E40000000000F3DE000000000000F3DE000082 +:10E8F40000000000F3DE000000000000F3DE000072 +:10E9040000000000F3DE000000000000F3DE000061 +:10E9140000000000F3DE000000000000F3DE000051 +:10E9240000000000F3DE000000000000F3DE000041 +:10E9340000000000F3DE000000000000F3DE000031 +:10E9440000000000F3DE000000000000F3DE000021 +:10E9540000000000F3DE000000000000F3DE000011 +:10E9640000000000F3DE000000000000F3DE000001 +:10E9740000000000F3DE000000000000F3DE0000F1 +:10E9840000000000F3DE000000000000F3DE0000E1 +:10E9940000000000F3DE000000000000F3DE0000D1 +:10E9A40000000000F3DE000000000000F3DE0000C1 +:10E9B40000000000F3DE000000000000F3DE0000B1 +:10E9C40000000000F3DE000000000000F3DE0000A1 +:10E9D40000000000F3DE000000000000F3DE000091 +:10E9E40000000000F3DE000000000000F3DE000081 +:10E9F40000000000F3DE000000000000F3DE000071 +:10EA040000000000F3DE000000000000F3DE000060 +:10EA140000000000F3DE000000000000F3DE000050 +:10EA240000000000F3DE000000000000F3DE000040 +:10EA340000000000F3DE000000000000F3DE000030 +:10EA440000000000F3DE000000000000F3DE000020 +:10EA540000000000F3DE000000000000F3DE000010 +:10EA640000000000F3DE000000000000F3DE000000 +:10EA740000000000F3DE000000000000F3DE0000F0 +:10EA840000000000F3DE000000000000F3DE0000E0 +:10EA940000000000F3DE000000000000F3DE0000D0 +:10EAA40000000000F3DE000000000000F3DE0000C0 +:10EAB40000000000F3DE000000000000F3DE0000B0 +:10EAC40000000000F3DE000000000000F3DE0000A0 +:10EAD40000000000F3DE000000000000F3DE000090 +:10EAE40000000000F3DE000000000000F3DE000080 +:10EAF40000000000F3DE000000000000F3DE000070 +:10EB040000000000F3DE000000000000F3DE00005F +:10EB140000000000F3DE000000000000F3DE00004F +:10EB240000000000F3DE000000000000F3DE00003F +:10EB340000000000F3DE000000000000F3DE00002F +:10EB440000000000F3DE000000000000F3DE00001F +:10EB540000000000F3DE000000000000F3DE00000F +:10EB640000000000F3DE000000000000F3DE0000FF +:10EB740000000000F3DE000000000000F3DE0000EF +:10EB840000000000F3DE000000000000F3DE0000DF +:10EB940000000000F3DE000000000000F3DE0000CF +:10EBA40000000000F3DE000000000000F3DE0000BF +:10EBB40000000000F3DE000000000000F3DE0000AF +:10EBC40000000000F3DE000000000000F3DE00009F +:10EBD40000000000F3DE000000000000F3DE00008F +:10EBE40000000000F3DE000000000000F3DE00007F +:10EBF40000000000F3DE000000000000F3DE00006F +:10EC040000000000F3DE000000000000F3DE00005E +:10EC140000000000F3DE000000000000F3DE00004E +:10EC240000000000F3DE000000000000F3DE00003E +:10EC340000000000F3DE000000000000F3DE00002E +:10EC440000000000F3DE000000000000F3DE00001E +:10EC540000000000F3DE000000000000F3DE00000E +:10EC640000000000F3DE000000000000F3DE0000FE +:10EC740000000000F3DE000000000000F3DE0000EE +:10EC840000000000F3DE000000000000F3DE0000DE +:10EC940000000000F3DE000000000000F3DE0000CE +:10ECA40000000000F3DE000000000000F3DE0000BE +:10ECB40000000000F3DE000000000000F3DE0000AE +:10ECC40000000000F3DE000000000000F3DE00009E +:10ECD40000000000F3DE000000000000F3DE00008E +:10ECE40000000000F3DE000000000000F3DE00007E +:10ECF40000000000F3DE000000000000F3DE00006E +:10ED040000000000F3DE000000000000F3DE00005D +:10ED140000000000F3DE000000000000F3DE00004D +:10ED240000000000F3DE000000000000F3DE00003D +:10ED340000000000F3DE000000000000F3DE00002D +:10ED440000000000F3DE000000000000F3DE00001D +:10ED540000000000F3DE000000000000F3DE00000D +:10ED640000000000F3DE000000000000F3DE0000FD +:10ED740000000000F3DE000000000000F3DE0000ED +:10ED840000000000F3DE000000000000F3DE0000DD +:10ED940000000000F3DE000000000000F3DE0000CD +:10EDA40000000000F3DE000000000000F3DE0000BD +:10EDB40000000000F3DE000000000000F3DE0000AD +:10EDC40000000000F3DE000000000000F3DE00009D +:10EDD40000000000F3DE000000000000F3DE00008D +:10EDE40000000000F3DE000000000000F3DE00007D +:10EDF40000000000F3DE000000000000F3DE00006D +:10EE040000000000F3DE000000000000F3DE00005C +:10EE140000000000F3DE000000000000F3DE00004C +:10EE240000000000F3DE000000000000F3DE00003C +:10EE340000000000F3DE000000000000F3DE00002C +:10EE440000E800004FE1000000000000F3DE0000D5 +:10EE540000000000F3DE000000000000F3DE00000C +:10EE640000000000F3DE000000000000F3DE0000FC +:10EE740000000000F3DE000000000000F3DE0000EC +:10EE840000000000F3DE000000000000F3DE0000DC +:10EE940000000000F3DE000000000000F3DE0000CC +:10EEA40000000000F3DE000000000000F3DE0000BC +:10EEB40000000000F3DE000000000000F3DE0000AC +:10EEC40000000000F3DE000000000000F3DE00009C +:10EED40000000000F3DE000000000000F3DE00008C +:10EEE40000000000F3DE000009C6000053E2000049 +:10EEF40000000000F3DE000000000000F3DE00006C +:10EF040000000000F3DE000000000000F3DE00005B +:10EF140000000000F3DE000000000000F3DE00004B +:10EF240000000000F3DE000000000000F3DE00003B +:10EF3400B5C9000053E2000000000000F3DE000049 +:10EF440000000000F3DE000000000000F3DE00001B +:10EF540000000000F3DE000000000000F3DE00000B +:10EF640000000000F3DE000000000000F3DE0000FB +:10EF740000000000F3DE000000000000F3DE0000EB +:10EF840000000000F3DE000000000000F3DE0000DB +:10EF940000000000F3DE000000000000F3DE0000CB +:10EFA40000000000F3DE000000000000F3DE0000BB +:10EFB40000000000F3DE000000000000F3DE0000AB +:10EFC40000000000F3DE000000000000F3DE00009B +:10EFD40000000000F3DE000000000000F3DE00008B +:10EFE40000000000F3DE000000000000F3DE00007B +:10EFF40000000000F3DE000000000000F3DE00006B +:10F0040000000000F3DE000000000000F3DE00005A +:10F0140000000000F3DE000000000000F3DE00004A +:10F0240000000000F3DE000000000000F3DE00003A +:10F0340000000000F3DE000000000000F3DE00002A +:10F0440000000000F3DE000000000000F3DE00001A +:10F0540000000000F3DE000000000000F3DE00000A +:10F0640000000000F3DE000000000000F3DE0000FA +:10F0740000000000F3DE000019C6000053E20000A7 +:08F08400E9BC000053E20000AA +:04F08C00FFFFFFFF84 +:10F09000DEE61E28EAAC45B802360000DEE61E2891 +:10F0A0004CBBCE8F0236000000A0105000820D50E5 +:10F0B00000040550050000000400000001000000ED +:10F0C0000338FDD870470000000000000000000079 +:10F0D0000338FDD870470000000000200000000049 +:10F0E0000600010002000000ECF00000000000003B +:10F0F000D8F2000006000000E04F160000000020DB +:10F10000E0F2000021000000E0EF022004000020F7 +:10F1100065AF000035AF000000000000B9B000008E +:10F12000A9AF00007DE0000083E0000039B00000DE +:10F13000FBDF00005DE000000000000097DF000042 +:10F14000000000000000000000A0105000820D50E0 +:10F15000000405501F00000000A010500000000037 +:10F160000000000000C0105001000000FFFF000080 +:10F1700000820D50000000000100000000A00D50B2 +:10F1800000000000FF070000000405500000000020 +:10F19000020000000000000000000000E5B20000D6 +:10F1A0008FE000009BE00000AFE00000B9E000004D +:10F1B000C3E00000C1B40000D7E000000000000080 +:10F1C00000A0105000820D500004055000600C504B +:10F1D0000000000000000000F4F1000000C2010087 +:10F1E0000000D60100000000000000400A040020DA +:10F1F0000904002000600C5000F200000100000033 +:10F2000008F20000040000002400000026000002B4 +:10F21000250600012706000367E1000031B7000062 +:10F2200059E1000000A0105000820D50000405506C +:10F2300060E7000060E7000098E70000A0E700003A +:10F24000B0E70000B0E700006E726635346C31350F +:10F25000646B2F6E726635346C31352F6370756157 +:10F2600070700048656C6C6F20576F726C64212061 +:10F2700025730A002A2A2A20426F6F74696E67205C +:10F280006E524620436F6E6E6563742053444B206C +:10F2900076322E382E39392D633065393632383989 +:10F2A00039336263202A2A2A0A002A2A2A2055731F +:10F2B000696E67205A6570687972204F53207633E3 +:10F2C0002E372E39392D3466336566306161303022 +:10F2D0006235202A2A2A0A00464C4153485F3000F2 +:10F2E0005352414D5F3000636C6F636B403130654A +:10F2F000303030006770696F40313061303030003D +:10F300006770696F406438323030006770696F40F1 +:10F3100035303430300000010405020608090F754D +:10F32000617274406336303030004552524F523A69 +:10F330002043616E6E6F742066756C66696C6C201C +:10F340004558545F41504920726571756573742E3C +:10F350000D0A005741524E494E473A204F7074698A +:10F360006F6E616C204558545F4150492072657141 +:10F3700075657374206E6F742066756C66696C6C4D +:10F3800065642E0D0A00000718232323232323235B +:10F390002323232323230001026D61696E00286E5D +:10F3A000756C6C29002A666C6F61742A000000007D +:04F3B00000980000C1 +:10F3B40003DD0000000000008DDF000000000000FD +:10F3C40000000000E5B7000000000000FF0000009E +:10F3D4000090D003000000000000000000000000C6 +:10F3E4000000000000000000000000000000000019 +:10F3F4000000000000000000000000000000000009 +:10F4040000000000000000000000000000000000F8 +:10F4140000000000000000000000000000000000E8 +:10F4240000000000000000000000000008000000D0 +:10F43400FF000000000000000200000000000000C7 +:10F4440000000000000000000000000000000000B8 +:10F4540000000000000000000000000000000000A8 +:10F464000000000000000000000000000000000098 +:10F474000000000000000000000000000000000088 +:10F484000000000000000000000000000000000078 +:10F494000000000000000000040000000F00000055 +:10F4A4000000000001000000000000000000000057 +:10F4B40067000000000000000000000000000000E1 +:10F4C4000000000000000000000000000000000038 +:10F4D4000000000000000000000000000000000028 +:10F4E4000000000000000000000000000000000018 +:10F4F4000000000000000000000000000000000008 +:08F5040050010020500100201D +:0AF50C0000000000000000000000F5 +:04F5160015E015E007 +:040000030000A6ED66 +:00000001FF diff --git a/scripts/bootloader/validation_data.py b/scripts/bootloader/validation_data.py old mode 100644 new mode 100755 index 44fc41266e0a..2bab9e5eebcf --- a/scripts/bootloader/validation_data.py +++ b/scripts/bootloader/validation_data.py @@ -4,85 +4,158 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +from __future__ import annotations -from intelhex import IntelHex - -import hashlib +import abc import argparse +import hashlib import struct -import ecdsa +import sys +from typing import TextIO, BinaryIO + +import ecdsa # type: ignore[import-untyped] +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ed25519 +from cryptography.hazmat.primitives.serialization import load_pem_public_key +from intelhex import IntelHex # type: ignore[import-untyped] + + +class BaseValidator(abc.ABC): + + def __init__(self, hashfunc) -> None: + """ + param hashfunc: hashing function e.g. hashlib.sha256 + """ + self.hashfunc = hashfunc + + def get_hash(self, input_hex: IntelHex) -> bytes: + firmware_bytes = input_hex.tobinstr() + return self.hashfunc(firmware_bytes).digest() + + @abc.abstractmethod + def to_string(self, public_key) -> bytes: + """Serialize the public key to bytes.""" + @abc.abstractmethod + def verify(self, public_key, signature: bytes, hash_bytes: bytes): + """Verify signature. Raise an exception if not valid.""" -def get_hash(input_hex): - firmware_bytes = input_hex.tobinstr() - return hashlib.sha256(firmware_bytes).digest() + def get_validation_data( + self, + signature_bytes: bytes, + input_hex: IntelHex, + public_key: ecdsa.VerifyingKey | ed25519.Ed25519PublicKey, + magic_value: bytes + ) -> bytes: + hash_bytes = self.get_hash(input_hex) + public_key_bytes = self.to_string(public_key) + # Will raise an exception if it fails + self.verify(public_key, signature_bytes, hash_bytes) -def get_validation_data(signature_bytes, input_hex, public_key, magic_value): - hash_bytes = get_hash(input_hex) - public_key_bytes = public_key.to_string() + validation_bytes = magic_value + validation_bytes += struct.pack('I', input_hex.addresses()[0]) + validation_bytes += hash_bytes + validation_bytes += public_key_bytes + validation_bytes += signature_bytes - # Will raise an exception if it fails - public_key.verify(signature_bytes, hash_bytes, hashfunc=hashlib.sha256) + return validation_bytes - validation_bytes = magic_value - validation_bytes += struct.pack('I', input_hex.addresses()[0]) - validation_bytes += hash_bytes - validation_bytes += public_key_bytes - validation_bytes += signature_bytes + def append_validation_data( + self, + signature: bytes, + input_file: TextIO, + public_key: ecdsa.VerifyingKey | ed25519.Ed25519PublicKey, + offset: int, + output_hex: TextIO, + output_bin: BinaryIO | None, + magic_value: str + ) -> None: + ih = IntelHex(input_file) + ih.start_addr = None # OBJCOPY incorrectly inserts x86 specific records, remove the start_addr as it is wrong. - return validation_bytes + minimum_offset = ((ih.maxaddr() // 4) + 1) * 4 + if offset != 0 and offset < minimum_offset: + raise RuntimeError(f'Incorrect offset, must be bigger than {hex(minimum_offset)}') + # Parse comma-separated string of uint32s into hex string. Each is encoded in little-endian byte order + parsed_magic_value = b''.join( + [struct.pack(' bytes: + return public_key.to_string() + + def verify(self, public_key: ecdsa.VerifyingKey, signature_bytes: bytes, hash_bytes: bytes): + public_key.verify(signature_bytes, hash_bytes, hashfunc=self.hashfunc) + + +class Ed25519SignatureValidator(BaseValidator): + + def to_string(self, public_key) -> bytes: + """Serialize the public key to bytes.""" + public_key_bytes = public_key.public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw + ) + return public_key_bytes + + def verify(self, public_key: ed25519.Ed25519PublicKey, signature_bytes: bytes, hash_bytes: bytes): + public_key.verify(signature_bytes, hash_bytes) def parse_args(): parser = argparse.ArgumentParser( description='Append validation metadata at specified offset. Generate HEX and BIN file', formatter_class=argparse.RawDescriptionHelpFormatter, - allow_abbrev=False) + allow_abbrev=False + ) + + parser.add_argument( + '--algorithm', '-a', dest='algorithm', help='Signing algorithm (default: %(default)s)', + choices=['ecdsa', 'ed25519'], default='ecdsa' + ) - parser.add_argument('-i', '--input', required=True, type=argparse.FileType('r', encoding='UTF-8'), + parser.add_argument('-i', '--input', required=True, + type=argparse.FileType('r', encoding='UTF-8'), help='Input hex file.') parser.add_argument('--offset', required=False, type=int, help='Offset to store validation metadata at.', default=0) parser.add_argument('-s', '--signature', required=True, type=argparse.FileType('rb'), - help="Signature file (DER) of ECDSA (secp256r1) signature of 'input' argument.") - parser.add_argument('-p', '--public-key', required=True, type=argparse.FileType('r', encoding='UTF-8'), + help="Signature file (DER) of ECDSA (secp256r1) or ED25519 signature of 'input' argument.") + parser.add_argument('-p', '--public-key', required=True, + type=argparse.FileType('r', encoding='UTF-8'), help='Public key file (PEM).') parser.add_argument('-m', '--magic-value', required=True, help='ASCII representation of magic value.') - parser.add_argument('-o', '--output-hex', required=False, default=None, type=argparse.FileType('w'), + parser.add_argument('-o', '--output-hex', required=False, default=None, + type=argparse.FileType('w'), help='.hex output file name. Default is to overwrite --input.') - parser.add_argument('--output-bin', required=False, default=None, type=argparse.FileType('w'), + parser.add_argument('--output-bin', required=False, default=None, + type=argparse.FileType('w'), help='.bin output file name.') args = parser.parse_args() @@ -92,18 +165,36 @@ def parse_args(): return args -def main(): - +def main() -> int: args = parse_args() - append_validation_data(signature=args.signature.read(), - input_file=args.input, - public_key=ecdsa.VerifyingKey.from_pem(args.public_key.read()), - offset=args.offset, - output_hex=args.output_hex, - output_bin=args.output_bin, - magic_value=args.magic_value) + if args.algorithm == 'ecdsa': + public_key = ecdsa.VerifyingKey.from_pem(args.public_key.read()) + EcdsaSignatureValidator(hashfunc=hashlib.sha256).append_validation_data( + signature=args.signature.read(), + input_file=args.input, + public_key=public_key, + offset=args.offset, + output_hex=args.output_hex, + output_bin=args.output_bin, + magic_value=args.magic_value + ) + elif args.algorithm == 'ed25519': + public_key = load_pem_public_key(args.public_key.read().encode()) + Ed25519SignatureValidator(hashfunc=hashlib.sha512).append_validation_data( + signature=args.signature.read(), + input_file=args.input, + public_key=public_key, + offset=args.offset, + output_hex=args.output_hex, + output_bin=args.output_bin, + magic_value=args.magic_value + ) + else: + raise SystemExit('Not implemented') + + return 0 if __name__ == '__main__': - main() + sys.exit(main())