From bb55470b0e0a2dd085a26cb1fbdb2428f3cfea4f Mon Sep 17 00:00:00 2001 From: nagendra modadugu Date: Wed, 4 Jan 2017 21:08:17 -0800 Subject: [PATCH] CR50: add a hardware backed GCM implementation This change adds hardware support for AES128-GCM along with a subset of NIST test vectors. BRANCH=none BUG=chrome-os-partner:60833 CQ-DEPEND=CL:411535 TEST=tpmtest.py passes Change-Id: I93445684f6a910c35a9117eac6cb19d28067a021 Signed-off-by: nagendra modadugu Reviewed-on: https://chromium-review.googlesource.com/425002 Commit-Ready: Nagendra Modadugu Tested-by: Marius Schilder Tested-by: Nagendra Modadugu Reviewed-by: Marius Schilder Reviewed-by: Vadim Bendebury --- board/cr50/tpm2/aes.c | 84 ++++++++- chip/g/build.mk | 1 + chip/g/dcrypto/dcrypto.h | 43 ++++- chip/g/dcrypto/gcm.c | 345 ++++++++++++++++++++++++++++++++++ chip/g/registers.h | 3 + test/tpm_test/crypto_test.py | 33 +++- test/tpm_test/crypto_test.xml | 267 ++++++++++++++++++++++++++ 7 files changed, 767 insertions(+), 9 deletions(-) create mode 100644 chip/g/dcrypto/gcm.c diff --git a/board/cr50/tpm2/aes.c b/board/cr50/tpm2/aes.c index 164f08aeb5..72148fb0ec 100644 --- a/board/cr50/tpm2/aes.c +++ b/board/cr50/tpm2/aes.c @@ -228,6 +228,8 @@ static void aes_command_handler(void *cmd_body, uint16_t key_len; uint8_t iv_len; uint8_t *iv; + uint8_t aad_len; + const uint8_t *aad; enum aes_test_cipher_mode c_mode; enum encrypt_mode e_mode; uint8_t *cmd = (uint8_t *)cmd_body; @@ -262,6 +264,8 @@ static void aes_command_handler(void *cmd_body, * key | key len | key to use * iv_len | 1 | either 0 or 16 * iv | 0 or 16 | as defined by iv_len + * aad_len | <= 127 | additional authentication data length + * aad | aad_len | additional authentication data * text_len | 2 | size of the text to process, big endian * text | text_len | text to encrypt/decrypt */ @@ -277,12 +281,16 @@ static void aes_command_handler(void *cmd_body, cmd += key_len; key_len *= 8; iv_len = *cmd++; - if (iv_len && (iv_len != 16)) { + if ((c_mode == TEST_MODE_GCM && iv_len == 0) || + (c_mode != TEST_MODE_GCM && iv_len && iv_len != 16)) { CPRINTF("Invalid vector len %d\n", iv_len); return; } iv = cmd; cmd += iv_len; + aad_len = *cmd++; + aad = cmd; + cmd += aad_len; data_len = *cmd++; data_len = data_len * 256 + *cmd++; @@ -388,6 +396,80 @@ static void aes_command_handler(void *cmd_body, } break; } + case TEST_MODE_GCM: + { + if (e_mode == 0) { + size_t total; + size_t count; + struct GCM_CTX ctx; + + DCRYPTO_gcm_init(&ctx, key_local.b, iv_local.b, iv_len); + DCRYPTO_gcm_aad(&ctx, aad, aad_len); + count = DCRYPTO_gcm_decrypt( + &ctx, out_local.b, sizeof(out_local.b), + data_local.b, data_len); + if (count < 0) { + CPRINTF( + "%s: gcm decrypt failed\n", __func__); + break; + } + total = count; + count = DCRYPTO_gcm_decrypt_final( + &ctx, out_local.b + total, + sizeof(out_local.b) - total); + if (count < 0) { + CPRINTF( + "%s: gcm decrypt_final failed\n", + __func__); + break; + } + total += count; + count = DCRYPTO_gcm_tag(&ctx, out_local.b + total, + sizeof(out_local.b) - total); + if (count == 0) { + CPRINTF("%s: gcm tag failed\n", __func__); + break; + } + total += count; + *response_size = total; + } else if (e_mode == 1) { + size_t total; + size_t count; + struct GCM_CTX ctx; + + DCRYPTO_gcm_init(&ctx, key_local.b, iv_local.b, iv_len); + DCRYPTO_gcm_aad(&ctx, aad, aad_len); + count = DCRYPTO_gcm_encrypt( + &ctx, out_local.b, sizeof(out_local.b), + data_local.b, data_len); + if (count < 0) { + CPRINTF( + "%s: gcm encrypt failed\n"); + break; + } + total = count; + count = DCRYPTO_gcm_encrypt_final( + &ctx, out_local.b + total, + sizeof(out_local.b) - total); + if (count < 0) { + CPRINTF( + "%s: gcm encrypt_final failed\n", + __func__); + break; + } + total += count; + count = DCRYPTO_gcm_tag( + &ctx, out_local.b + total, + sizeof(out_local.b) - total); + if (count == 0) { + CPRINTF("%s: gcm tag failed\n", __func__); + break; + } + total += count; + *response_size = total; + } + break; + } case TEST_MODE_OFB: if (e_mode == 0) { if (_cpri__AESDecryptOFB( diff --git a/chip/g/build.mk b/chip/g/build.mk index f52efee865..ffaf21e5ab 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -37,6 +37,7 @@ chip-$(CONFIG_DCRYPTO)+= dcrypto/bn.o chip-$(CONFIG_DCRYPTO)+= dcrypto/bn_hw.o chip-$(CONFIG_DCRYPTO)+= dcrypto/compare.o chip-$(CONFIG_DCRYPTO)+= dcrypto/dcrypto_runtime.o +chip-$(CONFIG_DCRYPTO)+= dcrypto/gcm.o chip-$(CONFIG_DCRYPTO)+= dcrypto/hkdf.o chip-$(CONFIG_DCRYPTO)+= dcrypto/hmac.o chip-$(CONFIG_DCRYPTO)+= dcrypto/key_ladder.o diff --git a/chip/g/dcrypto/dcrypto.h b/chip/g/dcrypto/dcrypto.h index 58f45ddcc9..b7980dee15 100644 --- a/chip/g/dcrypto/dcrypto.h +++ b/chip/g/dcrypto/dcrypto.h @@ -55,6 +55,48 @@ void DCRYPTO_aes_read_iv(uint8_t *iv); int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits, const uint8_t *iv, const uint8_t *in, size_t in_len); +/* AES-GCM-128 */ +struct GCM_CTX { + union { + uint32_t d[4]; + uint8_t c[16]; + } block, Ej0; + + uint64_t aad_len; + uint64_t count; + size_t remainder; +}; + +/* Initialize the GCM context structure. */ +void DCRYPTO_gcm_init(struct GCM_CTX *ctx, const uint8_t *key, + const uint8_t *iv, size_t iv_len); +/* Additional authentication data to include in the tag calculation. */ +void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len); +/* Encrypt & decrypt return the number of bytes written to out + * (always an integral multiple of 16), or -1 on error. These functions + * may be called repeatedly with incremental data. + * + * NOTE: if in_len is not a integral multiple of 16, then out_len must + * be atleast in_len - (in_len % 16) + 16 bytes. + */ +int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, + const uint8_t *in, size_t in_len); +int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, + const uint8_t *in, size_t in_len); +/* Encrypt & decrypt a partial final block, if any. These functions + * return the number of bytes written to out (<= 15), or -1 on error. + */ +int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx, + uint8_t *out, size_t out_len); +int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx, + uint8_t *out, size_t out_len); +/* Compute the tag over AAD + encrypt or decrypt data, and return the + * number of bytes written to tag. Returns -1 on error. + */ +int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len); +/* Cleanup secrets. */ +void DCRYPTO_gcm_finish(struct GCM_CTX *ctx); + /* * SHA implementation. This abstraction is backed by either a * software or hardware implementation. @@ -76,7 +118,6 @@ const uint8_t *DCRYPTO_SHA384_hash(const void *data, uint32_t n, uint8_t *digest); const uint8_t *DCRYPTO_SHA512_hash(const void *data, uint32_t n, uint8_t *digest); - /* * HMAC. */ diff --git a/chip/g/dcrypto/gcm.c b/chip/g/dcrypto/gcm.c new file mode 100644 index 0000000000..18016de612 --- /dev/null +++ b/chip/g/dcrypto/gcm.c @@ -0,0 +1,345 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "dcrypto.h" +#include "internal.h" +#include "registers.h" + +#include "endian.h" + +#include "cryptoc/util.h" + +static void gcm_mul(uint32_t *counter) +{ + int i; + volatile uint32_t *p; + + /* Set HASH to zero. */ + p = GREG32_ADDR(KEYMGR, GCM_HASH_IN0); + for (i = 0; i < 4; i++) + *p++ = 0; + + /* Initialize GMAC. */ + p = GREG32_ADDR(KEYMGR, GCM_MAC0); + for (i = 0; i < 4; i++) + *p++ = counter[i]; + + /* Crank GMAC. */ + GREG32(KEYMGR, GCM_DO_ACC) = 1; + + /* Read GMAC. */ + p = GREG32_ADDR(KEYMGR, GCM_MAC0); + for (i = 0; i < 4; i++) + counter[i] = *p++; + + /* Reset GMAC. */ + p = GREG32_ADDR(KEYMGR, GCM_MAC0); + for (i = 0; i < 4; ++i) + *p++ = 0; +} + +static void gcm_init_iv( + const uint8_t *iv, uint32_t iv_len, uint32_t *counter) +{ + + if (iv_len == 12) { + memcpy(counter, iv, 12); + counter[3] = 1 << 24; + } else { + size_t i; + uint32_t len = iv_len; + uint64_t len0 = len; + uint8_t *ctr = (uint8_t *) counter; + + memset(ctr, 0, 16); + while (len >= 16) { + for (i = 0; i < 16; ++i) + ctr[i] ^= iv[i]; + + gcm_mul(counter); + iv += 16; + len -= 16; + } + if (len) { + for (i = 0; i < len; ++i) + ctr[i] ^= iv[i]; + + gcm_mul(counter); + } + len0 <<= 3; + ctr[8] ^= (uint8_t)(len0 >> 56); + ctr[9] ^= (uint8_t)(len0 >> 48); + ctr[10] ^= (uint8_t)(len0 >> 40); + ctr[11] ^= (uint8_t)(len0 >> 32); + ctr[12] ^= (uint8_t)(len0 >> 24); + ctr[13] ^= (uint8_t)(len0 >> 16); + ctr[14] ^= (uint8_t)(len0 >> 8); + ctr[15] ^= (uint8_t)(len0); + + gcm_mul(counter); + } +} + +void DCRYPTO_gcm_init(struct GCM_CTX *ctx, const uint8_t *key, + const uint8_t *iv, size_t iv_len) +{ + int i; + const uint32_t zero[4] = {0, 0, 0, 0}; + uint32_t H[4]; + uint32_t counter[4]; + + memset(ctx, 0, sizeof(struct GCM_CTX)); + + /* Initialize AES engine in CTR mode, and set the counter to 0. */ + DCRYPTO_aes_init(key, 128, (const uint8_t *) zero, + CIPHER_MODE_CTR, ENCRYPT_MODE); + /* Set H to AES(ZERO). */ + DCRYPTO_aes_block((const uint8_t *) zero, (uint8_t *) H); + + /* Initialize the GMAC accumulator to ZERO. */ + for (i = 0; i < 4; i++) + GR_KEYMGR_GCM_MAC(i) = zero[i]; + + /* Initialize H. */ + for (i = 0; i < 4; i++) + GR_KEYMGR_GCM_H(i) = H[i]; + + /* Map the IV to a 128-bit counter. */ + gcm_init_iv(iv, iv_len, counter); + + /* Re-initialize the IV counter. */ + for (i = 0; i < 4; i++) + GR_KEYMGR_AES_CTR(i) = counter[i]; + + /* Calculate Ej0: encrypt IV counter XOR ZERO. */ + DCRYPTO_aes_block((const uint8_t *) zero, ctx->Ej0.c); +} + +static void gcm_aad_block(const struct GCM_CTX *ctx, const uint32_t *block) +{ + int i; + const struct access_helper *p = (struct access_helper *) block; + + if (ctx->aad_len == 0 && ctx->count <= 16) { + /* Update GMAC. */ + for (i = 0; i < 4; i++) + GR_KEYMGR_GCM_MAC(i) = p[i].udata; + } else { + for (i = 0; i < 4; i++) + GR_KEYMGR_GCM_HASH_IN(i) = p[i].udata; + + /* Crank GMAC. */ + GREG32(KEYMGR, GCM_DO_ACC) = 1; + } +} + +void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len) +{ + uint32_t block[4]; + + while (len) { + size_t count; + + memset(block, 0, sizeof(block)); + count = MIN(16, len); + memcpy(block, aad_data, count); + + gcm_aad_block(ctx, block); + ctx->aad_len += count; + + len -= count; + aad_data += count; + } + + always_memset(block, 0, sizeof(block)); +} + +int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, + const uint8_t *in, size_t in_len) +{ + uint8_t *outp = out; + + if (out_len < (in_len & ~0x0F) + ((in_len & 0x0F) ? 16 : 0)) + return -1; + + /* Process a previous partial block, if any. */ + if (ctx->remainder) { + size_t count = MIN(in_len, 16 - ctx->remainder); + + memcpy(ctx->block.c + ctx->remainder, in, count); + ctx->remainder += count; + if (ctx->remainder < 16) + return 0; + + DCRYPTO_aes_block(ctx->block.c, outp); + ctx->count += 16; + gcm_aad_block(ctx, (uint32_t *) outp); + ctx->remainder = 0; + in += count; + in_len -= count; + outp += 16; + } + + while (in_len >= 16) { + DCRYPTO_aes_block(in, outp); + ctx->count += 16; + + gcm_aad_block(ctx, (uint32_t *) outp); + + in_len -= 16; + in += 16; + outp += 16; + } + + if (in_len) { + memcpy(ctx->block.c, in, in_len); + ctx->remainder = in_len; + } + + return outp - out; +} + +int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx, uint8_t *out, size_t out_len) +{ + if (out_len < ctx->remainder) + return -1; + + if (ctx->remainder) { + size_t remainder = ctx->remainder; + uint8_t out_block[16]; + + DCRYPTO_aes_block(ctx->block.c, out_block); + ctx->count += ctx->remainder; + memcpy(out, out_block, ctx->remainder); + + memset(out_block + ctx->remainder, 0, 16 - ctx->remainder); + gcm_aad_block(ctx, (uint32_t *) out_block); + ctx->remainder = 0; + return remainder; + } + + return 0; +} + +int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, + const uint8_t *in, size_t in_len) +{ + uint8_t *outp = out; + + if (out_len < (in_len & ~0x0F) + ((in_len & 0x0F) ? 16 : 0)) + return -1; + + if (ctx->remainder) { + size_t count = MIN(in_len, 16 - ctx->remainder); + + memcpy(ctx->block.c + ctx->remainder, in, count); + ctx->remainder += count; + + if (ctx->remainder < 16) + return 0; + + DCRYPTO_aes_block(ctx->block.c, outp); + ctx->remainder = 0; + ctx->count += 16; + gcm_aad_block(ctx, ctx->block.d); + in += count; + in_len -= count; + outp += count; + } + + while (in_len >= 16) { + DCRYPTO_aes_block(in, outp); + ctx->count += 16; + gcm_aad_block(ctx, (uint32_t *) in); + in += 16; + in_len -= 16; + outp += 16; + } + + if (in_len) { + memcpy(ctx->block.c, in, in_len); + ctx->remainder = in_len; + } + + return outp - out; +} + +int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx, + uint8_t *out, size_t out_len) +{ + if (out_len < ctx->remainder) + return -1; + + if (ctx->remainder) { + size_t remainder = ctx->remainder; + uint8_t out_block[16]; + + DCRYPTO_aes_block(ctx->block.c, out_block); + ctx->count += ctx->remainder; + memcpy(out, out_block, ctx->remainder); + + memset(ctx->block.c + ctx->remainder, 0, 16 - ctx->remainder); + gcm_aad_block(ctx, ctx->block.d); + ctx->remainder = 0; + return remainder; + } + + return 0; +} + +static void dcrypto_gcm_len_vector( + const struct GCM_CTX *ctx, void *len_vector) { + uint64_t aad_be; + uint64_t count_be; + + /* Serialize counters to bit-count (big-endian). */ + aad_be = ctx->aad_len * 8; + aad_be = htobe64(aad_be); + count_be = ctx->count * 8; + count_be = htobe64(count_be); + + memcpy(len_vector, &aad_be, 8); + memcpy(((uint8_t *)len_vector) + 8, &count_be, 8); +} + +static void dcrypto_gcm_tag(const struct GCM_CTX *ctx, + const uint32_t *len_vector, uint32_t *tag) { + int i; + + for (i = 0; i < 4; i++) + GR_KEYMGR_GCM_HASH_IN(i) = len_vector[i]; + + /* Crank GMAC. */ + GREG32(KEYMGR, GCM_DO_ACC) = 1; + + for (i = 0; i < 4; i++) + GR_KEYMGR_GCM_HASH_IN(i) = ctx->Ej0.d[i]; + + /* Crank GMAC. */ + GREG32(KEYMGR, GCM_DO_ACC) = 1; + + /* Read tag. */ + for (i = 0; i < 4; i++) + tag[i] = GR_KEYMGR_GCM_MAC(i); +} + +int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len) +{ + uint32_t len_vector[4]; + uint32_t local_tag[4]; + size_t count = MIN(tag_len, sizeof(local_tag)); + + dcrypto_gcm_len_vector(ctx, len_vector); + dcrypto_gcm_tag(ctx, len_vector, local_tag); + + memcpy(tag, local_tag, count); + return count; +} + +void DCRYPTO_gcm_finish(struct GCM_CTX *ctx) +{ + always_memset(ctx, 0, sizeof(struct GCM_CTX)); + GREG32(KEYMGR, AES_WIPE_SECRETS) = 1; +} diff --git a/chip/g/registers.h b/chip/g/registers.h index 86f7a4ce81..dd063894e7 100644 --- a/chip/g/registers.h +++ b/chip/g/registers.h @@ -316,6 +316,9 @@ static inline int x_timehs_addr(unsigned int module, unsigned int timer, /* Key manager */ #define GR_KEYMGR_AES_KEY(n) REG32(GREG32_ADDR(KEYMGR, AES_KEY0) + (n)) #define GR_KEYMGR_AES_CTR(n) REG32(GREG32_ADDR(KEYMGR, AES_CTR0) + (n)) +#define GR_KEYMGR_GCM_H(n) REG32(GREG32_ADDR(KEYMGR, GCM_H0) + (n)) +#define GR_KEYMGR_GCM_HASH_IN(n) REG32(GREG32_ADDR(KEYMGR, GCM_HASH_IN0) + (n)) +#define GR_KEYMGR_GCM_MAC(n) REG32(GREG32_ADDR(KEYMGR, GCM_MAC0) + (n)) #define GR_KEYMGR_SHA_HASH(n) REG32(GREG32_ADDR(KEYMGR, SHA_STS_H0) + (n)) #define GR_KEYMGR_HKEY_FRR(n) REG32(GREG32_ADDR(KEYMGR, HKEY_FRR0) + (n)) diff --git a/test/tpm_test/crypto_test.py b/test/tpm_test/crypto_test.py index 1c292dcdbc..5790c1ee1c 100644 --- a/test/tpm_test/crypto_test.py +++ b/test/tpm_test/crypto_test.py @@ -36,7 +36,7 @@ def get_attribute(tdesc, attr_name, required=True): """ # Fields stored in hex format by default. - default_hex = ('cipher_text', 'iv', 'key') + default_hex = ('aad', 'cipher_text', 'iv', 'key', 'tag') data = tdesc.find(attr_name) if data is None: @@ -108,7 +108,7 @@ SUPPORTED_MODES = { }), } -def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm): +def crypto_run(node_name, op_type, key, iv, aad, in_text, out_text, tpm): """Perform a basic operation(encrypt or decrypt). This function creates an extended command with the requested parameters, @@ -125,6 +125,7 @@ def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm): directly to the device as a field in the extended command key: a binary string iv: a binary string, might be empty + aad: additional authenticated data in_text: a binary string, the input of the encrypt/decrypt operation out_text: a binary string, might be empty, the expected output of the operation. Note that it could be shorter than actual output (padded to @@ -156,6 +157,9 @@ def crypto_run(node_name, op_type, key, iv, in_text, out_text, tpm): cmd += '%c' % len(iv) if iv: cmd += iv + cmd += '%c' % len(aad) + if aad: + cmd += aad cmd += struct.pack('>H', len(in_text)) cmd += in_text if tpm.debug_enabled(): @@ -203,18 +207,33 @@ def crypto_test(tdesc, tpm): node_name, ''.join('%2.2x' % ord(x) for x in key))) iv = get_attribute(tdesc, 'iv', required=False) - if iv and len(iv) != 16: + if iv and not node_name.startswith('AES:GCM') and len(iv) != 16: raise subcmd.TpmTestError('wrong iv size "%s:%s"' % ( node_name, ''.join('%2.2x' % ord(x) for x in iv))) - clear_text = get_attribute(tdesc, 'clear_text') + clear_text = get_attribute(tdesc, 'clear_text', required=False) + if clear_text: + clear_text_len = get_attribute(tdesc, 'clear_text_len', required=False) + if clear_text_len: + clear_text = clear_text[:int(clear_text_len)] + else: + clear_text_len = None if tpm.debug_enabled(): print('clear text size', len(clear_text)) cipher_text = get_attribute(tdesc, 'cipher_text', required=False) + if clear_text_len: + cipher_text = cipher_text[:int(clear_text_len)] + tag = get_attribute(tdesc, 'tag', required=False) + aad = get_attribute(tdesc, 'aad', required=False) + if aad: + aad_len = get_attribute(tdesc, 'aad_len', required=False) + if aad_len: + aad = aad[:int(aad_len)] real_cipher_text = crypto_run(node_name, ENCRYPT, key, iv, - clear_text, cipher_text, tpm) - crypto_run(node_name, DECRYPT, key, iv, real_cipher_text, - clear_text, tpm) + aad or '', clear_text, cipher_text + tag, tpm) + crypto_run(node_name, DECRYPT, key, iv, aad or '', + real_cipher_text[:len(real_cipher_text) - len(tag)], + clear_text + tag, tpm) print(utils.cursor_back() + 'SUCCESS: %s' % node_name) def crypto_tests(tpm, xml_file): diff --git a/test/tpm_test/crypto_test.xml b/test/tpm_test/crypto_test.xml index 7cf0758749..4a9a9adea9 100644 --- a/test/tpm_test/crypto_test.xml +++ b/test/tpm_test/crypto_test.xml @@ -39,6 +39,7 @@ Many of the crypto_test elements were borrowed from NIST test vectors. 34b58f68 a9e27607 7bdd8e72 8b2b528b + 33221100 77665544 bbaa9988 ffeeddcc @@ -560,4 +561,270 @@ Many of the crypto_test elements were borrowed from NIST test vectors. + + + 53c372160082fa7a468a6b4c26d0f0f6 + + + 05 + + + 21d72a8e745f45f9313db5d88e7ef241 + + + + + + 74b5dd7fd041c2533aedfb3e1c374ec4 + + + 3f3a28eee37555c78748fd3e + + + f91d6db396e5d5b97f8b3ef8b31c9752 + + + b442ded5256c646129bd875ca2b9d362 + + + a5a4cd2c1eb95c410f2a5c13fdb2c978 + + + + + + 5571dcfeb2002d197598dd3cbaebb90d + + + f5746ba756171a5cb13883a0 + + + 55c43dd28798d4ceeae817c78429ecbe + + + c406d22a557e6e173688a02afa16688859e777fa + + + 5b433168f1da578848b113c5b5130d8290c42ca7989aa7bdd820f5a6d1393c76 + + + 0538821592a189da6f1dfc3b564d78878b12d1badedbff4da52fbbef685ec362 + + + + + + 7063be77e2c4718979cbd140eb7fd7e8 + + + 190fe0e001bad7fef397a736 + + + 8dcc9f2093ed753666719c8e46d99d70 + + + 1dec437a785a0a9c3365b1a0ab3c21a6 + + + + + + 79e8e399576e683ec585821d2b5ef764 + + + b80addc2a86ada68230d9cad + + + 10ba4f3f341faf0eaaadbab0855d99e9 + + + 2de468b6a84c444ed9fd3cb2d5ed9f5a21a58a17b0904814f53c73936c5222cf47ee17599a8041658c7a86c6fc099339 + + + + + + 8824a2d45c1dddf8d6a7196c4c9617ca + + + 7f83d5f3041aac22d5d1e025 + + + f9ac3ef273f8cdd186c526779c6e8248 + + + 24d4c5f1c6963fb88cb28cad470ed2a05a3b025e + + + 6a01437b97648916e67b45fb2241a5d2 + + + ee67bdc2ac5ce9f56eb0e327a8d03130 + + + + + + 635c7cbd562b54b771be0ea088156a33 + + + 231f72878e3c9cbabc1a57a5 + + + 2267b59c2e250b0d46ff7bb9f80fb0e4 + + + 7580eca60e37d3a0188959b7483eb9f36251474499b89749a9a67fa84e849f93b7a88a003b4c9f0e28d3191ae743f56bcbfe7b12a517ada1ccfc53ecacecfa266953c7c47daa8e4963ef6a702709004dbae4119b5e3e996c0000d99e + + + 90 + + + bbdd15de6121201ef69aa7e8f3c65aa5 + + + c028eb4162d7e4fe612397de80bc63c8 + , + + + + + 23deefdff02b12c689b50a37734b800e + + + 02a8d6920f679099e279de16 + + + 9982a39d07c5cd680dc3d1941545cd41 + + + 786db1a2c1e61d250e35ce9142f25e5c + + + 8c0a266478e97d2821756ce9000000d0 + + + 13 + + + 94de78bf177c848ab4d44936000000d0 + + + + + + dbb8d3cbce11fbfce60657341a8873cd + + + 68bb62dc5d9aecd041679d75 + + + b2215a420c5890ea03349188b54b912b + + + 61b64409f39462fe08bb2ac959b2c17e4edc32b068285f0c636ebe1c478f17c25af32643c280cad3e785348ec852b2e5 + + + 48 + + + f53bf8855bd5df982adebfc800000064 + + + 13 + + + 3b6b6f20ecdf32b040839fd3000000b1 + + + + + + 1007719909c0ab5969bdf2e438b39d86 + + + 5ea9a9079c1e82a35132c613 + + + 11d970781a81547ff1706934410c09de + + + 50c34bf56f4fed1fa85efb6d0bdf060182e636d85cb72562e8f622028359b359 + + + 59c156056cf34ef8452b60b10920b1261b6175c7c04db6ff9792cad9016a2ccd + + + + + + a17cbf8fdd25d52f58621ee9c251fe73 + + + 51ea0b20a190977b5eafadcf + + + 7407867cb93283f8bd6bcea727a77202 + + + 7fc014a4ec0be6e29e40cc9c6f9c899ebb8005e661c807262385f0f71bda9ce61d713a9c09359c1dc9e471176d99b9502e4fd00a10b3d1002a545358ff9fe0960fc82efc2887f88caef094f5a6984fb18b4e23882703f7fb000064b3 + + + 90 + + + 27e6d33963494b7c42160d840aaefae697dae25b554e1cf602ce57bbc4d40319 + + + f78e67fe95ac696f6d3b55db7aa0d5ad51e1c89da39f6afe1662cda16878b836 + + + + + + bb5315b6b9954885d01c75298403f8c5 + + + 99f963885de564ae5774bd0b + + + 4a5f9b5dc6c16898d86fbd9c4013f051 + + + f2b514d9e58cb0d1b39ca53e45725810 + + + 32111b9beac4d01712cf4379adc6693c36973c2e75c9518c55d1454186eee1ddffcac840975c7af1136ad237f34bee7e006d0969 + + + 51 + + + 63b4faac80258a2b3df11251bc82e085bd49dc89a84f169242d2daa2f2b2a1c3df2f6f695f0279f5a96e143f7de4a37d00654bc3 + + + + + + 2862503caa50f4680c99280936ebe115 + + + 90521d81e7578d76c7b67bd8 + + + fc5e9bbdcd0880a47d4f3a974438722c + + + e0c7e2da2bfdd3a319ca4ec0a07851b184cfb503c2280c890ff215a67f42db8a128c69707887efb2fb93110c7416cdb8 + + + f8a8d0ed19e93328bfe20f744acfde9edc726cc8ef0c4989aa83697b85fc99af7dc85c6c7c8af96327f31b86a86dea1f00ab156a + + + 51 + + + b02514a54b3b8d6005ecd446f0da1dca8920dd2bec5405ae841c2afbdc823dc6abb9dd71490b1f1b7cd22adee773512b00a60a00 + +