diff --git a/crypto/slh_dsa/build.info b/crypto/slh_dsa/build.info index d6e37a90c1..4f698d692a 100644 --- a/crypto/slh_dsa/build.info +++ b/crypto/slh_dsa/build.info @@ -1,6 +1,7 @@ LIBS=../../libcrypto -$COMMON=slh_dsa_key.c slh_params.c +$COMMON=slh_adrs.c slh_dsa.c slh_dsa_ctx.c slh_dsa_key.c slh_fors.c slh_hash.c \ + slh_hypertree.c slh_params.c slh_wots.c slh_xmss.c IF[{- !$disabled{'slh_dsa'} -}] SOURCE[../../libcrypto]=$COMMON diff --git a/crypto/slh_dsa/slh_adrs.c b/crypto/slh_dsa/slh_adrs.c new file mode 100644 index 0000000000..29b0882746 --- /dev/null +++ b/crypto/slh_dsa/slh_adrs.c @@ -0,0 +1,132 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include +#include "slh_adrs.h" + +/* See FIPS 205 - Section 4.3 Table 1 Uncompressed Addresses */ +#define SLH_ADRS_OFF_LAYER_ADR 0 +#define SLH_ADRS_OFF_TREE_ADR 4 +#define SLH_ADRS_OFF_TYPE 16 +#define SLH_ADRS_OFF_KEYPAIR_ADDR 20 +#define SLH_ADRS_OFF_CHAIN_ADDR 24 +#define SLH_ADRS_OFF_HASH_ADDR 28 +#define SLH_ADRS_OFF_TREE_INDEX SLH_ADRS_OFF_HASH_ADDR +#define SLH_ADRS_SIZE_TYPE 4 +/* Number of bytes after type to clear */ +#define SLH_ADRS_SIZE_TYPECLEAR SLH_ADRS_SIZE - (SLH_ADRS_OFF_TYPE + SLH_ADRS_SIZE_TYPE) +#define SLH_ADRS_SIZE_KEYPAIR_ADDR 4 + +/* See FIPS 205 - Section 11.2 Table 3 Compressed Addresses */ +#define SLH_ADRSC_OFF_LAYER_ADR 0 +#define SLH_ADRSC_OFF_TREE_ADR 1 +#define SLH_ADRSC_OFF_TYPE 9 +#define SLH_ADRSC_OFF_KEYPAIR_ADDR 10 +#define SLH_ADRSC_OFF_CHAIN_ADDR 14 +#define SLH_ADRSC_OFF_HASH_ADDR 18 +#define SLH_ADRSC_OFF_TREE_INDEX SLH_ADRSC_OFF_HASH_ADDR +#define SLH_ADRSC_SIZE_TYPE 1 +#define SLH_ADRSC_SIZE_TYPECLEAR SLH_ADRS_SIZE_TYPECLEAR +#define SLH_ADRSC_SIZE_KEYPAIR_ADDR SLH_ADRS_SIZE_KEYPAIR_ADDR + +#define slh_adrsc_set_tree_height slh_adrsc_set_chain_address +#define slh_adrsc_set_tree_index slh_adrsc_set_hash_address + +static OSSL_SLH_ADRS_FUNC_set_layer_address slh_adrsc_set_layer_address; +static OSSL_SLH_ADRS_FUNC_set_tree_address slh_adrsc_set_tree_address; +static OSSL_SLH_ADRS_FUNC_set_type_and_clear slh_adrsc_set_type_and_clear; +static OSSL_SLH_ADRS_FUNC_set_keypair_address slh_adrsc_set_keypair_address; +static OSSL_SLH_ADRS_FUNC_copy_keypair_address slh_adrsc_copy_keypair_address; +static OSSL_SLH_ADRS_FUNC_set_chain_address slh_adrsc_set_chain_address; +static OSSL_SLH_ADRS_FUNC_set_hash_address slh_adrsc_set_hash_address; +static OSSL_SLH_ADRS_FUNC_zero slh_adrsc_zero; +static OSSL_SLH_ADRS_FUNC_copy slh_adrsc_copy; + +/* Variants of the FIPS 205 Algorithm 3 toByte(x, n) for 32 and 64 bit integers */ + +/* Convert a 32 bit value |in| to 4 bytes |out| in big endian format */ +static ossl_inline void U32TOSTR(unsigned char *out, uint32_t in) +{ + out[3] = (unsigned char)((in) & 0xff); + out[2] = (unsigned char)((in >> 8) & 0xff); + out[1] = (unsigned char)((in >> 16) & 0xff); + out[0] = (unsigned char)((in >> 24) & 0xff); +} + +/* Convert a 64 bit value |in| to 8 bytes |out| in big endian format */ +static ossl_inline void U64TOSTR(unsigned char *out, uint64_t in) +{ + out[7] = (unsigned char)((in) & 0xff); + out[6] = (unsigned char)((in >> 8) & 0xff); + out[5] = (unsigned char)((in >> 16) & 0xff); + out[4] = (unsigned char)((in >> 24) & 0xff); + out[3] = (unsigned char)((in >> 32) & 0xff); + out[2] = (unsigned char)((in >> 40) & 0xff); + out[1] = (unsigned char)((in >> 48) & 0xff); + out[0] = (unsigned char)((in >> 56) & 0xff); +} + +/* Compressed versions of ADRS functions See Table 3 */ +static void slh_adrsc_set_layer_address(SLH_ADRS adrsc, uint32_t layer) +{ + adrsc[SLH_ADRSC_OFF_LAYER_ADR] = (uint8_t)layer; +} +static void slh_adrsc_set_tree_address(SLH_ADRS adrsc, uint64_t in) +{ + U64TOSTR(adrsc + SLH_ADRSC_OFF_TREE_ADR, in); +} +static void slh_adrsc_set_type_and_clear(SLH_ADRS adrsc, uint32_t type) +{ + adrsc[SLH_ADRSC_OFF_TYPE] = (uint8_t)type; + memset(adrsc + SLH_ADRSC_OFF_TYPE + SLH_ADRSC_SIZE_TYPE, 0, SLH_ADRSC_SIZE_TYPECLEAR); +} +static void slh_adrsc_set_keypair_address(SLH_ADRS adrsc, uint32_t in) +{ + U32TOSTR(adrsc + SLH_ADRSC_OFF_KEYPAIR_ADDR, in); +} +static void slh_adrsc_copy_keypair_address(SLH_ADRS dst, const SLH_ADRS src) +{ + memcpy(dst + SLH_ADRSC_OFF_KEYPAIR_ADDR, src + SLH_ADRSC_OFF_KEYPAIR_ADDR, + SLH_ADRSC_SIZE_KEYPAIR_ADDR); +} +static void slh_adrsc_set_chain_address(SLH_ADRS adrsc, uint32_t in) +{ + U32TOSTR(adrsc + SLH_ADRSC_OFF_CHAIN_ADDR, in); +} +static void slh_adrsc_set_hash_address(SLH_ADRS adrsc, uint32_t in) +{ + U32TOSTR(adrsc + SLH_ADRSC_OFF_HASH_ADDR, in); +} +static void slh_adrsc_zero(SLH_ADRS adrsc) +{ + memset(adrsc, 0, SLH_ADRSC_SIZE); +} +static void slh_adrsc_copy(SLH_ADRS dst, const SLH_ADRS src) +{ + memcpy(dst, src, SLH_ADRSC_SIZE); +} + +const SLH_ADRS_FUNC *ossl_slh_get_adrs_fn(int is_compressed) +{ + static const SLH_ADRS_FUNC methods[] = { + { + slh_adrsc_set_layer_address, + slh_adrsc_set_tree_address, + slh_adrsc_set_type_and_clear, + slh_adrsc_set_keypair_address, + slh_adrsc_copy_keypair_address, + slh_adrsc_set_chain_address, + slh_adrsc_set_tree_height, + slh_adrsc_set_hash_address, + slh_adrsc_set_tree_index, + slh_adrsc_zero, + slh_adrsc_copy, + } + }; + return &methods[0]; +} diff --git a/crypto/slh_dsa/slh_adrs.h b/crypto/slh_dsa/slh_adrs.h new file mode 100644 index 0000000000..2c7acf8de2 --- /dev/null +++ b/crypto/slh_dsa/slh_adrs.h @@ -0,0 +1,73 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +/* + * An Address object is used to store a blob of data that is used by hash + * functions. It stores information related to the type of operation, as well as + * information related to tree addresses and heights. + * SHAKE based algorithms use 32 bytes for this object, whereas SHA2 based + * algorithms use a compressed format of 22 bytes. For this reason there are + * different method tables to support the different formats. + * FIPS 205 Section 4.2 describes the SHAKE related functions. + * The compressed format is discussed in Section 11.2. + */ + +#define SLH_ADRS_SIZE 32 /* size of the ADRS blob */ +#define SLH_ADRSC_SIZE 22 /* size of a compact ADRS blob */ +#define SLH_ADRS_SIZE_MAX SLH_ADRS_SIZE + +/* 7 Different types of addresses */ +#define SLH_ADRS_TYPE_WOTS_HASH 0 +#define SLH_ADRS_TYPE_WOTS_PK 1 +#define SLH_ADRS_TYPE_TREE 2 +#define SLH_ADRS_TYPE_FORS_TREE 3 +#define SLH_ADRS_TYPE_FORS_ROOTS 4 +#define SLH_ADRS_TYPE_WOTS_PRF 5 +#define SLH_ADRS_TYPE_FORS_PRF 6 + +typedef uint8_t *SLH_ADRS; + +#define SLH_ADRS_DECLARE(a) uint8_t a[SLH_ADRS_SIZE_MAX] +#define SLH_ADRS_FUNC_DECLARE(ctx, adrsf) \ + const SLH_ADRS_FUNC *adrsf = ctx->adrs_func +#define SLH_ADRS_FN_DECLARE(adrsf, t) OSSL_SLH_ADRS_FUNC_##t *t = adrsf->t + +typedef void (OSSL_SLH_ADRS_FUNC_zero)(SLH_ADRS adrs); +typedef void (OSSL_SLH_ADRS_FUNC_copy)(SLH_ADRS dst, const SLH_ADRS src); +typedef void (OSSL_SLH_ADRS_FUNC_copy_keypair_address)(SLH_ADRS dst, const SLH_ADRS src); +/* + * Note that the tree address is actually 12 bytes in uncompressed format, + * but we only use 8 bytes + */ +typedef void (OSSL_SLH_ADRS_FUNC_set_tree_address)(SLH_ADRS adrs, uint64_t in); +typedef void (OSSL_SLH_ADRS_FUNC_set_layer_address)(SLH_ADRS adrs, uint32_t layer); +typedef void (OSSL_SLH_ADRS_FUNC_set_type_and_clear)(SLH_ADRS adrs, uint32_t type); +typedef void (OSSL_SLH_ADRS_FUNC_set_keypair_address)(SLH_ADRS adrs, uint32_t in); +typedef void (OSSL_SLH_ADRS_FUNC_set_chain_address)(SLH_ADRS adrs, uint32_t in); +typedef void (OSSL_SLH_ADRS_FUNC_set_tree_height)(SLH_ADRS adrs, uint32_t in); +typedef void (OSSL_SLH_ADRS_FUNC_set_hash_address)(SLH_ADRS adrs, uint32_t in); +typedef void (OSSL_SLH_ADRS_FUNC_set_tree_index)(SLH_ADRS adrs, uint32_t in); + +typedef struct slh_adrs_func_st { + OSSL_SLH_ADRS_FUNC_set_layer_address *set_layer_address; + OSSL_SLH_ADRS_FUNC_set_tree_address *set_tree_address; + OSSL_SLH_ADRS_FUNC_set_type_and_clear *set_type_and_clear; + OSSL_SLH_ADRS_FUNC_set_keypair_address *set_keypair_address; + OSSL_SLH_ADRS_FUNC_copy_keypair_address *copy_keypair_address; + OSSL_SLH_ADRS_FUNC_set_chain_address *set_chain_address; + OSSL_SLH_ADRS_FUNC_set_tree_height *set_tree_height; + OSSL_SLH_ADRS_FUNC_set_hash_address *set_hash_address; + OSSL_SLH_ADRS_FUNC_set_tree_index *set_tree_index; + OSSL_SLH_ADRS_FUNC_zero *zero; + OSSL_SLH_ADRS_FUNC_copy *copy; +} SLH_ADRS_FUNC; + +const SLH_ADRS_FUNC *ossl_slh_get_adrs_fn(int is_compressed); diff --git a/crypto/slh_dsa/slh_dsa.c b/crypto/slh_dsa/slh_dsa.c new file mode 100644 index 0000000000..b3951bf2b6 --- /dev/null +++ b/crypto/slh_dsa/slh_dsa.c @@ -0,0 +1,169 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include +#include +#include +#include "slh_dsa_local.h" +#include "slh_dsa_key.h" + +#define SLH_MAX_M 49 + +/* (n + SLH_SIG_FORS_LEN(k, a, n) + SLH_SIG_HT_LEN(n, hm, d)) */ +#define SLH_SIG_RANDOM_LEN(n) (n) +#define SLH_SIG_FORS_LEN(k, a, n) (n) * ((k) * (1 + (a))) +#define SLH_SIG_HT_LEN(h, d, n) (n) * ((h) + (d) * SLH_WOTS_LEN(n)) + +static void get_tree_ids(const uint8_t *digest, const SLH_DSA_PARAMS *params, + uint64_t *tree_id, uint32_t *leaf_id); + +static int slh_verify_internal(SLH_DSA_CTX *ctx, const SLH_DSA_KEY *pub, + const uint8_t *msg, size_t msg_len, + const uint8_t *sig, size_t sig_len) +{ + SLH_HASH_FUNC_DECLARE(ctx, hashf, hctx); + SLH_ADRS_FUNC_DECLARE(ctx, adrsf); + SLH_ADRS_DECLARE(adrs); + uint8_t mdigest[SLH_MAX_M]; + uint8_t pk_fors[SLH_MAX_N]; + uint64_t tree_id; + uint32_t leaf_id; + const SLH_DSA_PARAMS *params = ctx->params; + uint32_t n = params->n; + size_t r_len = SLH_SIG_RANDOM_LEN(n); + size_t sig_fors_len = SLH_SIG_FORS_LEN(params->k, params->a, n); + size_t sig_ht_len = SLH_SIG_HT_LEN(params->h, params->d, n); + const uint8_t *r, *sig_fors, *sig_ht, *md, *pk_seed, *pk_root; + + if (sig_len != (r_len + sig_fors_len + sig_ht_len)) + return 0; + /* Exit if public key is not set */ + if (pub->key_len == 0) + return 0; + + adrsf->zero(adrs); + + r = sig; + sig_fors = r + r_len; + sig_ht = sig_fors + sig_fors_len; + + pk_seed = SLH_DSA_PK_SEED(pub); + pk_root = SLH_DSA_PK_ROOT(pub); + + hashf->H_MSG(hctx, r, pk_seed, pk_root, msg, msg_len, mdigest); + md = mdigest; + get_tree_ids(mdigest, params, &tree_id, &leaf_id); + + adrsf->set_tree_address(adrs, tree_id); + adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_FORS_TREE); + adrsf->set_keypair_address(adrs, leaf_id); + ossl_slh_fors_pk_from_sig(ctx, sig_fors, md, pk_seed, adrs, pk_fors); + return ossl_slh_ht_verify(ctx, pk_fors, sig_ht, pk_seed, tree_id, leaf_id, pk_root); +} + +/* + * Pure signatures M' function + * ctx is the empty string by default. + */ +static uint8_t *msg_encode(const uint8_t *msg, size_t msg_len, + const uint8_t *ctx, size_t ctx_len, int encode, + uint8_t *tmp, size_t tmp_len, size_t *out_len) +{ + uint8_t *encoded = NULL; + size_t encoded_len; + + if (encode == 0) { + /* Raw message */ + *out_len = msg_len; + return (uint8_t *)msg; + } + if (ctx_len > SLH_DSA_MAX_CONTEXT_STRING_LEN) + return 0; + + /* Pure encoding */ + encoded_len = 1 + 1 + ctx_len + msg_len; + *out_len = encoded_len; + if (encoded_len <= tmp_len) { + encoded = tmp; + } else { + encoded = OPENSSL_zalloc(encoded_len); + if (encoded == NULL) + return NULL; + } + encoded[0] = 0; + encoded[1] = (uint8_t)ctx_len; + memcpy(&encoded[2], ctx, ctx_len); + memcpy(&encoded[2 + ctx_len], msg, msg_len); + return encoded; +} + +int ossl_slh_dsa_verify(SLH_DSA_CTX *slh_ctx, const SLH_DSA_KEY *pub, + const uint8_t *msg, size_t msg_len, + const uint8_t *ctx, size_t ctx_len, int encode, + const uint8_t *sig, size_t sig_len) +{ + uint8_t *m; + size_t m_len; + uint8_t m_tmp[1024]; + int ret = 0; + + m = msg_encode(msg, msg_len, ctx, ctx_len, encode, m_tmp, sizeof(m_tmp), + &m_len); + if (m == NULL) + return 0; + + ret = slh_verify_internal(slh_ctx, pub, m, m_len, sig, sig_len); + if (m != msg && m != m_tmp) + OPENSSL_free(m); + return ret; +} + +/* FIPS 205 Algorithm 2 toInt(X, n) */ +static uint64_t bytes_to_u64_be(const uint8_t *in, size_t in_len) +{ + size_t i; + uint64_t total = 0; + + for (i = 0; i < in_len; i++) + total = (total << 8) + *in++; + return total; +} + +/* + * See Algorithm 19 Steps 7..10 (also Algorithm 20 Step 10..13). + * Converts digested bytes into a tree index, and leaf index within the tree. + * The sizes are determined by the |params| parameter set. + */ +static void get_tree_ids(const uint8_t *digest, const SLH_DSA_PARAMS *params, + uint64_t *tree_id, uint32_t *leaf_id) +{ + const uint8_t *tree_id_bytes, *leaf_id_bytes; + uint32_t md_len, tree_id_len, leaf_id_len; + uint64_t tree_id_mask, leaf_id_mask; + + md_len = ((params->k * params->a + 7) >> 3); /* 21..40 bytes */ + tree_id_len = ((params->h - params->hm + 7) >> 3); /* 7 or 8 bytes */ + leaf_id_len = ((params->hm + 7) >> 3); /* 1 or 2 bytes */ + + tree_id_bytes = digest + md_len; + leaf_id_bytes = tree_id_bytes + tree_id_len; + + assert((md_len + tree_id_len + leaf_id_len) == params->m); + /* + * In order to calculate A mod (2^X) where X is in the range of (54..64) + * This is equivalent to A & (2^x - 1) which is just a sequence of X ones + * that must fit into a 64 bit value. + * e.g when X = 64 it would be A & (0xFFFF_FFFF_FFFF_FFFF) + * when X = 54 it would be A & (0x3F_FFFF_FFFF_FFFF) + * i.e. A & (0xFFFF_FFFF_FFFF_FFFF >> (64 - X)) + */ + tree_id_mask = ((uint64_t)-1) >> (64 - (params->h - params->hm)); + leaf_id_mask = (1 << params->hm) - 1; /* max value is 0x1FF when hm = 9 */ + *tree_id = bytes_to_u64_be(tree_id_bytes, tree_id_len) & tree_id_mask; + *leaf_id = (uint32_t)(bytes_to_u64_be(leaf_id_bytes, leaf_id_len) & leaf_id_mask); +} diff --git a/crypto/slh_dsa/slh_dsa_ctx.c b/crypto/slh_dsa/slh_dsa_ctx.c new file mode 100644 index 0000000000..385e57e8cd --- /dev/null +++ b/crypto/slh_dsa/slh_dsa_ctx.c @@ -0,0 +1,46 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include +#include +#include "slh_dsa_local.h" + +/* + * @param + */ +SLH_DSA_CTX *ossl_slh_dsa_ctx_new(const char *alg, + OSSL_LIB_CTX *lib_ctx, const char *propq) +{ + SLH_DSA_CTX *ret = OPENSSL_zalloc(sizeof(*ret)); + + if (ret != NULL) { + const SLH_DSA_PARAMS *params = ossl_slh_dsa_params_get(alg); + + if (params == NULL) + goto err; + ret->params = params; + ret->hash_func = ossl_slh_get_hash_fn(params->is_shake); + ret->adrs_func = ossl_slh_get_adrs_fn(params->is_shake == 0); + + if (!ossl_slh_hash_ctx_init(&ret->hash_ctx, lib_ctx, propq, + params->is_shake, + params->security_category, + params->n, params->m)) + goto err; + } + return ret; + err: + OPENSSL_free(ret); + return NULL; +} + +void ossl_slh_dsa_ctx_free(SLH_DSA_CTX *ctx) +{ + ossl_slh_hash_ctx_cleanup(&ctx->hash_ctx); + OPENSSL_free(ctx); +} diff --git a/crypto/slh_dsa/slh_dsa_key.c b/crypto/slh_dsa/slh_dsa_key.c index 5ccb646690..9dce013290 100644 --- a/crypto/slh_dsa/slh_dsa_key.c +++ b/crypto/slh_dsa/slh_dsa_key.c @@ -140,3 +140,8 @@ int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM params[]) key->key_len = 0; return 0; } + +int ossl_slh_dsa_key_type_matches(SLH_DSA_CTX *ctx, const SLH_DSA_KEY *key) +{ + return (key->params == ctx->params); +} diff --git a/crypto/slh_dsa/slh_dsa_key.h b/crypto/slh_dsa/slh_dsa_key.h index 4247ebd729..fa18f8a5d3 100644 --- a/crypto/slh_dsa/slh_dsa_key.h +++ b/crypto/slh_dsa/slh_dsa_key.h @@ -11,6 +11,8 @@ #include "internal/refcount.h" #define SLH_DSA_MAX_KEYLEN 32 * 2 /* 2 * n */ +#define SLH_DSA_PK_SEED(key) (key->pub) +#define SLH_DSA_PK_ROOT(key) (key->pub + key->params->n) struct slh_dsa_key_st { uint8_t pub[SLH_DSA_MAX_KEYLEN]; diff --git a/crypto/slh_dsa/slh_dsa_local.h b/crypto/slh_dsa/slh_dsa_local.h index 066318f541..f291665a98 100644 --- a/crypto/slh_dsa/slh_dsa_local.h +++ b/crypto/slh_dsa/slh_dsa_local.h @@ -8,4 +8,58 @@ */ #include "crypto/slh_dsa.h" +#include "slh_hash.h" #include "slh_params.h" + +/* + * Maximum size of the security parameter |n| in FIPS 205 Section 11. Table 2. + * This indicates the length in bytes of a message that can be signed. + * It is the size used by WOTS+ public and private key elements as well as + * signature elements. + */ +#define SLH_MAX_N 32 +/* + * For the given standard w=16 for all parameter sets. + * A n byte message is converted into 2 * n base 16 Integers followed + * by 3 Integers for the checksum of these values. + */ +#define SLH_WOTS_LEN(n) (2 * (n) + 3) + +/* + * FIPS 205 SLH_DSA algorithms have many different parameters which includes: + * - A set of constants (Section 11. contains 12 parameter sets) + * such as tree heights and security parameters associated with a algorithm + * name such as SLH-DSA-SHA2-128s. + * - ADRS functions (such as set_layer_address() in Section 4.3 & 11.2) + * - Hash Functions (such as H_MSG() & PRF()) See Sections 11.1, 11.2.1 & 11.2.2. + * + * - OpenSSL also uses an SLH_HASH_CTX to pass pre-fetched EVP related objects + * to the Hash functions. + * + * SLH_DSA_CTX is a container to hold all of these objects. This object is + * resolved early and is then passed to most SLH_DSA related functions. + */ +struct slh_dsa_ctx_st { + const SLH_DSA_PARAMS *params; + const SLH_ADRS_FUNC *adrs_func; + const SLH_HASH_FUNC *hash_func; + SLH_HASH_CTX hash_ctx; +}; + +void ossl_slh_wots_pk_from_sig(SLH_DSA_CTX *ctx, + const uint8_t *sig, const uint8_t *msg, + const uint8_t *pk_seed, uint8_t *adrs, + uint8_t *pk_out); + +void ossl_slh_xmss_pk_from_sig(SLH_DSA_CTX *ctx, uint32_t node_id, + const uint8_t *sig, const uint8_t *msg, + const uint8_t *pk_seed, SLH_ADRS adrs, + uint8_t *pk_out); + +int ossl_slh_ht_verify(SLH_DSA_CTX *ctx, const uint8_t *msg, const uint8_t *sig, + const uint8_t *pk_seed, uint64_t tree_id, uint32_t leaf_id, + const uint8_t *pk_root); + +void ossl_slh_fors_pk_from_sig(SLH_DSA_CTX *ctx, const uint8_t *sig, + const uint8_t *md, const uint8_t *pk_seed, + SLH_ADRS adrs, uint8_t *pk_out); diff --git a/crypto/slh_dsa/slh_fors.c b/crypto/slh_dsa/slh_fors.c new file mode 100644 index 0000000000..ab591b41b7 --- /dev/null +++ b/crypto/slh_dsa/slh_fors.c @@ -0,0 +1,121 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "slh_dsa_local.h" + +/* k = 14, 17, 22, 33, 35 (number of trees) */ +#define SLH_MAX_K 35 +/* a = 6, 8, 9, 12 or 14 - There are (2^a) merkle trees */ +#define SLH_MAX_A 9 + +#define SLH_MAX_K_TIMES_A (SLH_MAX_A * SLH_MAX_K) +#define SLH_MAX_ROOTS (SLH_MAX_K_TIMES_A * SLH_MAX_N) + +static void slh_base_2b(const uint8_t *in, uint32_t b, uint32_t *out, size_t out_len); + +/** + * @brief Compute a candidatr FORS public key from a message and signature. + * See FIPS 205 Section 8.4 Algorithm 17. + * + * @param sig A FORS signature of size (k * (a + 1) * n) bytes + * @param md A message digest of size (k * a / 8) bytes + * @param pk_seed A public key seed of size |n| + * @param adrs An ADRS object containing + * @param pk_out The returned + */ +void ossl_slh_fors_pk_from_sig(SLH_DSA_CTX *ctx, const uint8_t *sig, + const uint8_t *md, const uint8_t *pk_seed, + SLH_ADRS adrs, uint8_t *pk_out) +{ + SLH_ADRS_DECLARE(pk_adrs); + SLH_ADRS_FUNC_DECLARE(ctx, adrsf); + SLH_ADRS_FN_DECLARE(adrsf, set_tree_index); + SLH_ADRS_FN_DECLARE(adrsf, set_tree_height); + SLH_HASH_FUNC_DECLARE(ctx, hashf, hctx); + SLH_HASH_FN_DECLARE(hashf, F); + SLH_HASH_FN_DECLARE(hashf, H); + uint32_t i, j, aoff = 0; + uint32_t ids[SLH_MAX_K]; + uint8_t roots[SLH_MAX_ROOTS], *node = roots; + const SLH_DSA_PARAMS *params = ctx->params; + uint32_t a = params->a; + uint32_t k = params->k; + uint32_t n = params->n; + uint32_t two_power_a = (1 << a); + + /* Split md into k a-bit values e.g ids[0..k-1] = 12 bits each of md */ + slh_base_2b(md, a, ids, k); + + /* Compute the roots of k Merkle trees */ + for (i = 0; i < k; ++i) { + uint32_t id = ids[i]; + uint32_t node_id = id + aoff; + + set_tree_height(adrs, 0); + set_tree_index(adrs, node_id); + F(hctx, pk_seed, adrs, sig, n, node); + sig += n; + + for (j = 0; j < a; ++j) { + set_tree_height(adrs, j + 1); + if ((id & 1) == 0) { + node_id >>= 1; + set_tree_index(adrs, node_id); + H(hctx, pk_seed, adrs, node, sig, node); + } else { + node_id = (node_id - 1) >> 1; + set_tree_index(adrs, node_id); + H(hctx, pk_seed, adrs, sig, node, node); + } + id >>= 1; + sig += n; + } + aoff += two_power_a; + node += n; + } + assert((size_t)(node - roots) <= sizeof(roots)); + + /* The public key is the hash of all the roots of the k trees */ + adrsf->copy(pk_adrs, adrs); + adrsf->set_type_and_clear(pk_adrs, SLH_ADRS_TYPE_FORS_ROOTS); + adrsf->copy_keypair_address(pk_adrs, adrs); + hashf->T(hctx, pk_seed, pk_adrs, roots, node - roots, pk_out); +} + +/** + * @brief Convert a byte string into a base 2^b representation + * (See FIPS 205 Algorithm 4) + * + * @param in An input byte stream with a size >= |outlen * b / 8| + * @param b The bit size to divide |in| into + * This is one of 6, 8, 9, 12 or 14 for FORS. + * @param out The array of returned base-2^b integers that represents the first + * |outlen|*|b| bits of |in| + * @param outlen The size of |out| + * + */ +static void slh_base_2b(const uint8_t *in, uint32_t b, uint32_t *out, size_t out_len) +{ + size_t consumed = 0; + uint32_t bits = 0; + uint32_t total = 0; + uint32_t mask = (1 << b) - 1; + + for (consumed = 0; consumed < out_len; consumed++) { + while (bits < b) { + total <<= 8; + total += *in++; + bits += 8; + } + bits -= b; + *out++ = (total >> bits) & mask; + } +} diff --git a/crypto/slh_dsa/slh_hash.c b/crypto/slh_dsa/slh_hash.c new file mode 100644 index 0000000000..dab5218c31 --- /dev/null +++ b/crypto/slh_dsa/slh_hash.c @@ -0,0 +1,262 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/deprecated.h" /* PKCS1_MGF1() */ + +#include +#include +#include +#include +#include /* PKCS1_MGF1() */ +#include "slh_dsa_local.h" + +#define MAX_DIGEST_SIZE 64 /* SHA-512 is used for security category 3 & 5 */ +/* + * PRF(), F() use this value to calculate the number of zeros + * H(), T() also use this for security cat 1 + */ +#define SHA2_NUM_ZEROS_BOUND1 64 +/* H(), T() use this to calculate the number of zeros for security cat 3 & 5 */ +#define SHA2_NUM_ZEROS_BOUND2 128 + +static OSSL_SLH_HASHFUNC_H_MSG slh_hmsg_sha2; +static OSSL_SLH_HASHFUNC_PRF slh_prf_sha2; +static OSSL_SLH_HASHFUNC_PRF_MSG slh_prf_msg_sha2; +static OSSL_SLH_HASHFUNC_F slh_f_sha2; +static OSSL_SLH_HASHFUNC_H slh_h_sha2; +static OSSL_SLH_HASHFUNC_T slh_t_sha2; + +static EVP_MAC_CTX *hmac_ctx_new(OSSL_LIB_CTX *lib_ctx, const char *propq) +{ + EVP_MAC_CTX *mctx = NULL; + EVP_MAC *mac = EVP_MAC_fetch(lib_ctx, "HMAC", propq); + + if (mac == NULL) + return NULL; + mctx = EVP_MAC_CTX_new(mac); + EVP_MAC_free(mac); + return mctx; +} + +static EVP_MD_CTX *md_ctx_new(EVP_MD *md) +{ + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + + if (ctx == NULL) + return NULL; + + if (EVP_DigestInit_ex2(ctx, md, NULL) != 1) { + EVP_MD_CTX_free(ctx); + ctx = NULL; + } + return ctx; +} + +int ossl_slh_hash_ctx_init(SLH_HASH_CTX *ctx, OSSL_LIB_CTX *lib_ctx, + const char *propq, int is_shake, + int security_category, size_t n, size_t m) +{ + const char *digest_alg = is_shake ? "SHAKE-256" : "SHA2-256"; + + ctx->md = EVP_MD_fetch(lib_ctx, digest_alg, propq); + if (ctx->md == NULL) + return 0; + /* For SHA2 all categories require a SHA2-256 digest */ + ctx->md_ctx = md_ctx_new(ctx->md); + if (ctx->md_ctx == NULL) + goto err; + + /* + * SHA2 algorithm(s) require SHA256 + HMAC_SHA(X) & MGF1(SHAX) + * SHAKE algorithm(s) use SHAKE for all functions. + */ + if (is_shake == 0) { + if (security_category == 1) { + ctx->sha2_h_and_t_bound = SHA2_NUM_ZEROS_BOUND1; + /* For category 1 SHA2-256 is used for all hash operations */ + ctx->md_big_ctx = ctx->md_ctx; + ctx->hmac_digest = "SHA2-256"; + } else { + /* Security categories 3 & 5 also need SHA-512 */ + EVP_MD_free(ctx->md); + ctx->md = EVP_MD_fetch(lib_ctx, "SHA2-512", propq); + if (ctx->md == NULL) + goto err; + ctx->sha2_h_and_t_bound = SHA2_NUM_ZEROS_BOUND2; + /* Use HMAC-SHA2-512 for PRF_MSG */ + ctx->hmac_digest = "SHA2-512"; + /* use SHA2-512 in H_MSG, H and T */ + ctx->md_big_ctx = md_ctx_new(ctx->md); + if (ctx->md_big_ctx == NULL) + goto err; + /* PRF & F use SHA2-256 via ctx->md_ctx */ + + } + /* This assumes that propq exists for the duration of the operation */ + ctx->hmac_propq = propq; + ctx->hmac_ctx = hmac_ctx_new(lib_ctx, propq); + if (ctx->hmac_ctx == NULL) + goto err; + } + ctx->n = n; + ctx->m = m; + return 1; + err: + ossl_slh_hash_ctx_cleanup(ctx); + return 0; +} + +void ossl_slh_hash_ctx_cleanup(SLH_HASH_CTX *ctx) +{ + EVP_MD_free(ctx->md); + EVP_MAC_CTX_free(ctx->hmac_ctx); + if (ctx->md_big_ctx != ctx->md_ctx) + EVP_MD_CTX_free(ctx->md_big_ctx); + EVP_MD_CTX_free(ctx->md_ctx); +} + +static ossl_inline int +digest_4(EVP_MD_CTX *ctx, + const uint8_t *in1, size_t in1_len, const uint8_t *in2, size_t in2_len, + const uint8_t *in3, size_t in3_len, const uint8_t *in4, size_t in4_len, + uint8_t *out) +{ + return (EVP_DigestInit_ex2(ctx, NULL, NULL) == 1 + && EVP_DigestUpdate(ctx, in1, in1_len) == 1 + && EVP_DigestUpdate(ctx, in2, in2_len) == 1 + && EVP_DigestUpdate(ctx, in3, in3_len) == 1 + && EVP_DigestUpdate(ctx, in4, in4_len) == 1 + && EVP_DigestFinal_ex(ctx, out, NULL) == 1); +} + +/* FIPS 205 Section 11.2.1 and 11.2.2 */ + +static void +slh_hmsg_sha2(SLH_HASH_CTX *hctx, const uint8_t *r, const uint8_t *pk_seed, + const uint8_t *pk_root, const uint8_t *msg, size_t msg_len, + uint8_t *out) +{ + size_t n = hctx->n; + uint8_t seed[2 * SLH_MAX_N + MAX_DIGEST_SIZE]; + int sz = EVP_MD_get_size(hctx->md); + size_t seed_len = (size_t)sz + 2 * n; + + assert(sz > 0); + assert(seed_len <= sizeof(seed)); + + memcpy(seed, r, n); + memcpy(seed + n, pk_seed, n); + digest_4(hctx->md_big_ctx, r, n, pk_seed, n, pk_root, n, msg, msg_len, + seed + 2 * n); + PKCS1_MGF1(out, hctx->m, seed, seed_len, hctx->md); +} + +static void +slh_prf_msg_sha2(SLH_HASH_CTX *hctx, + const uint8_t *sk_prf, const uint8_t *opt_rand, + const uint8_t *msg, size_t msg_len, uint8_t *out) +{ + EVP_MAC_CTX *mctx = hctx->hmac_ctx; + size_t n = hctx->n; + uint8_t mac[MAX_DIGEST_SIZE]; + OSSL_PARAM *p = NULL; + OSSL_PARAM params[3]; + + /* + * Due to the way HMAC works, it is not possible to do this code early + * in hmac_ctx_new() since it requires a key in order to set the digest. + */ + if (hctx->hmac_digest != NULL) { + p = params; + /* The underlying digest to be used */ + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + (char *)hctx->hmac_digest, 0); + if (hctx->hmac_propq != NULL) + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_PROPERTIES, + (char *)hctx->hmac_propq, 0); + *p = OSSL_PARAM_construct_end(); + p = params; + hctx->hmac_digest = NULL; + } + + EVP_MAC_init(mctx, sk_prf, n, p); + EVP_MAC_update(mctx, opt_rand, n); + EVP_MAC_update(mctx, msg, msg_len); + EVP_MAC_final(mctx, mac, NULL, sizeof(mac)); + memcpy(out, mac, n); /* Truncate output to n bytes */ +} + +static ossl_inline void +do_hash(EVP_MD_CTX *ctx, size_t n, const uint8_t *pk_seed, const SLH_ADRS adrs, + const uint8_t *m, size_t m_len, size_t b, uint8_t *out) +{ + uint8_t zeros[128] = { 0 }; + uint8_t digest[MAX_DIGEST_SIZE]; + + assert(b - n < sizeof(zeros)); + + digest_4(ctx, pk_seed, n, zeros, b - n, adrs, SLH_ADRSC_SIZE, m, m_len, + digest); + /* Truncated returned value is n = 16 bytes */ + memcpy(out, digest, n); +} + +static void +slh_prf_sha2(SLH_HASH_CTX *hctx, const uint8_t *pk_seed, + const uint8_t *sk_seed, const SLH_ADRS adrs, uint8_t *out) +{ + size_t n = hctx->n; + + do_hash(hctx->md_ctx, n, pk_seed, adrs, sk_seed, n, + SHA2_NUM_ZEROS_BOUND1, out); +} + +static void +slh_f_sha2(SLH_HASH_CTX *hctx, const uint8_t *pk_seed, const SLH_ADRS adrs, + const uint8_t *m1, size_t m1_len, uint8_t *out) +{ + do_hash(hctx->md_ctx, hctx->n, pk_seed, adrs, m1, m1_len, + SHA2_NUM_ZEROS_BOUND1, out); +} + +static void +slh_h_sha2(SLH_HASH_CTX *hctx, const uint8_t *pk_seed, const SLH_ADRS adrs, + const uint8_t *m1, const uint8_t *m2, uint8_t *out) +{ + uint8_t m[SLH_MAX_N * 2]; + size_t n = hctx->n; + + memcpy(m, m1, n); + memcpy(m + n, m2, n); + do_hash(hctx->md_big_ctx, n, pk_seed, adrs, m, 2 * n, + hctx->sha2_h_and_t_bound, out); +} + +static void +slh_t_sha2(SLH_HASH_CTX *hctx, const uint8_t *pk_seed, const SLH_ADRS adrs, + const uint8_t *ml, size_t ml_len, uint8_t *out) +{ + do_hash(hctx->md_big_ctx, hctx->n, pk_seed, adrs, ml, ml_len, + hctx->sha2_h_and_t_bound, out); +} + +const SLH_HASH_FUNC *ossl_slh_get_hash_fn(int is_shake) +{ + static const SLH_HASH_FUNC methods[] = { + { + slh_hmsg_sha2, + slh_prf_sha2, + slh_prf_msg_sha2, + slh_f_sha2, + slh_h_sha2, + slh_t_sha2 + } + }; + return &methods[0]; +} diff --git a/crypto/slh_dsa/slh_hash.h b/crypto/slh_dsa/slh_hash.h new file mode 100644 index 0000000000..e226db73cc --- /dev/null +++ b/crypto/slh_dsa/slh_hash.h @@ -0,0 +1,76 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_CRYPTO_SLH_HASH_H +# define OSSL_CRYPTO_SLH_HASH_H +# pragma once + +# include +# include "slh_adrs.h" + +# define SLH_HASH_FUNC_DECLARE(ctx, hashf, hashctx) \ + const SLH_HASH_FUNC *hashf = ctx->hash_func; \ + SLH_HASH_CTX *hashctx = &ctx->hash_ctx + +# define SLH_HASH_FN_DECLARE(hashf, t) OSSL_SLH_HASHFUNC_##t * t = hashf->t + +/* See FIPS 205 Section 11.1 */ + +typedef struct slh_hash_ctx_st { + EVP_MD_CTX *md_ctx; /* Used for SHAKE and SHA-256 */ + EVP_MD_CTX *md_big_ctx; /* Used for SHA-256 or SHA-512 */ + EVP_MAC_CTX *hmac_ctx; + /* Stupid HMAC can't be set up early since the key is required */ + const char *hmac_digest; + const char *hmac_propq; + EVP_MD *md; /* Used by the MGF1 */ + size_t n; /* The output size of a HASH - this truncates in some cases */ + size_t m; /* The output size of the HMSG */ + size_t sha2_h_and_t_bound; +} SLH_HASH_CTX; + +/* + * @params out is |m| bytes which ranges from (30..49) bytes + */ +typedef void (OSSL_SLH_HASHFUNC_H_MSG)(SLH_HASH_CTX *ctx, const uint8_t *r, + const uint8_t *pk_seed, const uint8_t *pk_root, + const uint8_t *msg, size_t msg_len, uint8_t *out); + +typedef void (OSSL_SLH_HASHFUNC_PRF)(SLH_HASH_CTX *ctx, const uint8_t *pk_seed, + const uint8_t *sk_seed, const SLH_ADRS adrs, uint8_t *out); + +typedef void (OSSL_SLH_HASHFUNC_PRF_MSG)(SLH_HASH_CTX *ctx, const uint8_t *sk_prf, + const uint8_t *opt_rand, const uint8_t *msg, size_t msg_len, uint8_t *out); + +typedef void (OSSL_SLH_HASHFUNC_F)(SLH_HASH_CTX *ctx, const uint8_t *pk_seed, + const SLH_ADRS adrs, const uint8_t *m1, size_t m1_len, uint8_t *out); + +typedef void (OSSL_SLH_HASHFUNC_H)(SLH_HASH_CTX *ctx, const uint8_t *pk_seed, + const SLH_ADRS adrs, const uint8_t *m1, const uint8_t *m2, uint8_t *out); + +typedef void (OSSL_SLH_HASHFUNC_T)(SLH_HASH_CTX *ctx, const uint8_t *pk_seed, + const SLH_ADRS adrs, const uint8_t *m1, size_t m1_len, uint8_t *out); + +typedef struct slh_hash_func_st { + OSSL_SLH_HASHFUNC_H_MSG *H_MSG; + OSSL_SLH_HASHFUNC_PRF *PRF; + OSSL_SLH_HASHFUNC_PRF_MSG *PRF_MSG; + OSSL_SLH_HASHFUNC_F *F; + OSSL_SLH_HASHFUNC_H *H; + OSSL_SLH_HASHFUNC_T *T; +} SLH_HASH_FUNC; + +const SLH_HASH_FUNC *ossl_slh_get_hash_fn(int is_shake); + +int ossl_slh_hash_ctx_init(SLH_HASH_CTX *ctx, OSSL_LIB_CTX *libctx, + const char *propq, int is_shake, + int security_category, size_t n, size_t m); +void ossl_slh_hash_ctx_cleanup(SLH_HASH_CTX *ctx); + +#endif diff --git a/crypto/slh_dsa/slh_hypertree.c b/crypto/slh_dsa/slh_hypertree.c new file mode 100644 index 0000000000..6dfcd21912 --- /dev/null +++ b/crypto/slh_dsa/slh_hypertree.c @@ -0,0 +1,57 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "slh_dsa_local.h" + +#define SLH_XMSS_SIG_LEN(n, hm) ((SLH_WOTS_LEN(n) + (hm)) * (n)) + +/** + * @brief + * + * @param ctx Contains SLH_DSA algorithm functions and constants. + * @param msg A message of size |n| bytes + * @param sig A HT signature of size (|h| + |d| * |len|) * |n| bytes + * @param pk_seed SLH_DSA public key seed of size |n| + * @param tree_id Index of the XMSS tree that signed the message + * @param leaf_id Index of the WOTS+ key within the XMSS tree that signed the message + * @param pk_root The known Hypertree public key of size |n| + * + * @returns 1 if the computed XMSS public key matches pk_root, or 0 otherwise. + */ +int ossl_slh_ht_verify(SLH_DSA_CTX *ctx, const uint8_t *msg, const uint8_t *sig, + const uint8_t *pk_seed, uint64_t tree_id, uint32_t leaf_id, + const uint8_t *pk_root) +{ + SLH_ADRS_FUNC_DECLARE(ctx, adrsf); + SLH_ADRS_DECLARE(adrs); + uint8_t node[SLH_MAX_N]; + uint32_t layer, len, mask, d, n, tree_height; + const SLH_DSA_PARAMS *params = ctx->params; + + tree_height = params->hm; + n = params->n; + d = params->d; + len = SLH_XMSS_SIG_LEN(n, tree_height); + mask = (1 << tree_height) - 1; + + adrsf->zero(adrs); + memcpy(node, msg, n); + + for (layer = 0; layer < d; ++layer) { + adrsf->set_layer_address(adrs, layer); + adrsf->set_tree_address(adrs, tree_id); + ossl_slh_xmss_pk_from_sig(ctx, leaf_id, sig, node, pk_seed, adrs, node); + sig += len; + leaf_id = tree_id & mask; + tree_id >>= tree_height; + } + return (memcmp(node, pk_root, n) == 0); +} diff --git a/crypto/slh_dsa/slh_wots.c b/crypto/slh_dsa/slh_wots.c new file mode 100644 index 0000000000..616f713ce1 --- /dev/null +++ b/crypto/slh_dsa/slh_wots.c @@ -0,0 +1,157 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "slh_dsa_local.h" + +/* For the parameter sets defined there is only one w value */ +#define SLH_WOTS_LOGW 4 +#define SLH_WOTS_W 16 +#define SLH_WOTS_LEN1(n) (2 * (n)) +#define SLH_WOTS_LEN2 3 +#define SLH_WOTS_CHECKSUM_LEN ((SLH_WOTS_LEN2 + SLH_WOTS_LOGW + 7) / 8) +#define SLH_WOTS_LEN_MAX SLH_WOTS_LEN(SLH_MAX_N) +#define NIBBLE_MASK 15 +#define NIBBLE_SHIFT 4 + +/* + * @brief Convert a byte array to a byte array of (4 bit) nibbles + * This is a Variant of the FIPS 205 Algorithm 4 base_2^b function. + * It assumes that |in_len| is an even number and b is 4 bits. + * + * @param in A byte message to convert + * @param in_len The size of |in|. + * @param out The returned array of nibbles, with a size of 2*|in_len| + */ +static ossl_inline void slh_bytes_to_nibbles(const uint8_t *in, size_t in_len, + uint8_t *out) +{ + size_t consumed = 0; + + assert((in_len & 1) == 0); + + for (consumed = 0; consumed < in_len; consumed++) { + *out++ = (*in >> NIBBLE_SHIFT); + *out++ = (*in++ & NIBBLE_MASK); + } +} + +/* + * With w = 16 the maximum checksum is 0xF * n which fits into 12 bits + * which is 3 nibbles. + * + * This is effectively a cutdown version of Algorithm 7: steps 3 to 6 + * which does a complicated base2^b(tobyte()) operation. + */ +static ossl_inline void compute_checksum_nibbles(const uint8_t *in, size_t in_len, + uint8_t *out) +{ + size_t i; + uint16_t csum = 0; + + /* Compute checksum */ + for (i = 0; i < in_len; ++i) + csum += in[i]; + /* + * This line is effectively the same as doing csum += NIBBLE_MASK - in[i] + * in the loop above. + */ + csum = (uint16_t)(NIBBLE_MASK * in_len) - csum; + + /* output checksum as 3 nibbles */ + out[0] = (csum >> (2 * NIBBLE_SHIFT)) & NIBBLE_MASK; + out[1] = (csum >> NIBBLE_SHIFT) & NIBBLE_MASK; + out[2] = csum & NIBBLE_MASK; +} + +/** + * @brief WOTS+ Chaining function + * See FIPS 205 Section 5 Algorithm 5 + * + * Iterates using a hash function on the input |steps| times starting at index + * |start|. (Internally the |adrs| hash address is used to update the chaining + * index). + * + * @param ctx Contains SLH_DSA algorithm functions and constants. + * @param in An input string of |n| bytes + * @param n The size of |in| and |pk_seed|_ + * @param start_index The chaining start index + * @param steps The number of iterations starting from |start_index| + * Note |start_index| + |steps| < w + * (where w = 16 indicates the length of the hash chains) + * @param adrs An ADRS object which has a type of WOTS_HASH, and has a layer + * address, tree address, key pair address and chain address + * @param pk_seed A public key seed (which is added to the hash) + */ +static void slh_wots_chain(SLH_DSA_CTX *ctx, const uint8_t *in, + uint8_t start_index, uint8_t steps, + const uint8_t *pk_seed, uint8_t *adrs, uint8_t *out) +{ + SLH_HASH_FUNC_DECLARE(ctx, hashf, hctx); + SLH_ADRS_FUNC_DECLARE(ctx, adrsf); + SLH_HASH_FN_DECLARE(hashf, F); + SLH_ADRS_FN_DECLARE(adrsf, set_hash_address); + size_t j, end_index = start_index + steps; + size_t n = ctx->params->n; + + memcpy(out, in, n); + + for (j = start_index; j < end_index; ++j) { + set_hash_address(adrs, j); + F(hctx, pk_seed, adrs, out, n, out); + } +} + +/** + * @brief Compute a candidate WOTS+ public key from a message and signature + * See FIPS 205 Section 5.2 Algorithm 7 + * + * @param ctx Contains SLH_DSA algorithm functions and constants. + * @param sig A WOTS+signature of size len * |n| bytes. (where len = 2 * |n| + 3) + * @param msg A message of size |n| bytes. + * @param pk_seed The public key seed of size |n|. + * @param adrs An ADRS object containing the layer address, tree address and + * key pair address that of the WOTS+ key used to sign the message. + * @param pk_out The returned public key candidate of size |n| + */ +void ossl_slh_wots_pk_from_sig(SLH_DSA_CTX *ctx, + const uint8_t *sig, const uint8_t *msg, + const uint8_t *pk_seed, uint8_t *adrs, + uint8_t *pk_out) +{ + SLH_HASH_FUNC_DECLARE(ctx, hashf, hctx); + SLH_ADRS_FUNC_DECLARE(ctx, adrsf); + SLH_ADRS_FN_DECLARE(adrsf, set_chain_address); + SLH_ADRS_DECLARE(wots_pk_adrs); + uint8_t msg_and_csum_nibbles[SLH_WOTS_LEN_MAX]; + uint8_t tmp[SLH_WOTS_LEN_MAX * SLH_MAX_N], *ptmp = tmp; + size_t i, len1, len, n = ctx->params->n; + + len1 = SLH_WOTS_LEN1(n); + len = len1 + SLH_WOTS_LEN2; + + slh_bytes_to_nibbles(msg, n, msg_and_csum_nibbles); + compute_checksum_nibbles(msg_and_csum_nibbles, len1, msg_and_csum_nibbles + len1); + + /* Compute the end nodes for each of the chains */ + for (i = 0; i < len; ++i) { + set_chain_address(adrs, i); + slh_wots_chain(ctx, sig, msg_and_csum_nibbles[i], + NIBBLE_MASK - msg_and_csum_nibbles[i], + pk_seed, adrs, ptmp); + sig += n; + ptmp += n; + } + /* compress the computed public key value */ + adrsf->copy(wots_pk_adrs, adrs); + adrsf->set_type_and_clear(wots_pk_adrs, SLH_ADRS_TYPE_WOTS_PK); + adrsf->copy_keypair_address(wots_pk_adrs, adrs); + hashf->T(hctx, pk_seed, wots_pk_adrs, tmp, ptmp - tmp, pk_out); +} diff --git a/crypto/slh_dsa/slh_xmss.c b/crypto/slh_dsa/slh_xmss.c new file mode 100644 index 0000000000..ab2e0b3046 --- /dev/null +++ b/crypto/slh_dsa/slh_xmss.c @@ -0,0 +1,66 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "slh_dsa_local.h" + +/** + * @brief Compute a candidate XMSS public key from a message and XMSS signature + * + * @param sig A XMSS signature which consists of a WOTS+ signature of + * [2 * n + 3][n] bytes followed by an authentication path of + * [hm][n] bytes (where hm is the height of the XMSS tree). + * @param msg A message of size |n| bytes + * @param sk_seed A private key seed + * @param pk_seed A public key seed + * @param n The hash size size if the size of |msg|, |sk_seed| and |pk_seed| + * @param adrs An ADRS object containing a layer address and tress address of an + * XMSS key used for signing the message. + * @param node_id Must be set to the |node_id| used in xmss_sign(). + * @param tree_height The height of the XMSS tree. + * @param pk_out The returned candidate XMSS public key of size |n|. + */ +void ossl_slh_xmss_pk_from_sig(SLH_DSA_CTX *ctx, uint32_t node_id, + const uint8_t *sig, const uint8_t *msg, + const uint8_t *pk_seed, SLH_ADRS adrs, + uint8_t *pk_out) +{ + SLH_HASH_FUNC_DECLARE(ctx, hashf, hctx); + SLH_HASH_FN_DECLARE(hashf, H); + SLH_ADRS_FUNC_DECLARE(ctx, adrsf); + SLH_ADRS_FN_DECLARE(adrsf, set_tree_index); + SLH_ADRS_FN_DECLARE(adrsf, set_tree_height); + uint32_t k; + size_t n = ctx->params->n; + uint32_t hm = ctx->params->hm; + size_t wots_sig_len = n * SLH_WOTS_LEN(n); + const uint8_t *auth_path = sig + wots_sig_len; + uint8_t *node = pk_out; + + adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_WOTS_HASH); + adrsf->set_keypair_address(adrs, node_id); + ossl_slh_wots_pk_from_sig(ctx, sig, msg, pk_seed, adrs, node); + + adrsf->set_type_and_clear(adrs, SLH_ADRS_TYPE_TREE); + + for (k = 0; k < hm; ++k) { + set_tree_height(adrs, k + 1); + if ((node_id & 1) == 0) { /* even */ + node_id >>= 1; + set_tree_index(adrs, node_id); + H(hctx, pk_seed, adrs, node, auth_path, node); + } else { /* odd */ + node_id = (node_id - 1) >> 1; + set_tree_index(adrs, node_id); + H(hctx, pk_seed, adrs, auth_path, node, node); + } + auth_path += n; + } +} diff --git a/include/crypto/slh_dsa.h b/include/crypto/slh_dsa.h index 5065aa444e..62b30f0d91 100644 --- a/include/crypto/slh_dsa.h +++ b/include/crypto/slh_dsa.h @@ -17,6 +17,10 @@ # include # include "crypto/types.h" +# define SLH_DSA_MAX_CONTEXT_STRING_LEN 255 + +typedef struct slh_dsa_ctx_st SLH_DSA_CTX; + SLH_DSA_KEY *ossl_slh_dsa_key_new(OSSL_LIB_CTX *libctx, const char *alg); void ossl_slh_dsa_key_free(SLH_DSA_KEY *key); int ossl_slh_dsa_key_up_ref(SLH_DSA_KEY *key); @@ -25,4 +29,15 @@ int ossl_slh_dsa_key_equal(const SLH_DSA_KEY *key1, const SLH_DSA_KEY *key2, int ossl_slh_dsa_key_has(const SLH_DSA_KEY *key, int selection); int ossl_slh_dsa_key_fromdata(SLH_DSA_KEY *key, const OSSL_PARAM *params); +int ossl_slh_dsa_key_type_matches(SLH_DSA_CTX *ctx, const SLH_DSA_KEY *key); + +SLH_DSA_CTX *ossl_slh_dsa_ctx_new(const char *alg, + OSSL_LIB_CTX *lib_ctx, const char *propq); +void ossl_slh_dsa_ctx_free(SLH_DSA_CTX *ctx); + +int ossl_slh_dsa_verify(SLH_DSA_CTX *slh_ctx, const SLH_DSA_KEY *pub, + const uint8_t *msg, size_t msg_len, + const uint8_t *ctx, size_t ctx_len, int encode, + const uint8_t *sig, size_t sig_len); + #endif /* OSSL_CRYPTO_SLH_DSA_H */ diff --git a/providers/defltprov.c b/providers/defltprov.c index 6aea57219e..9924a01131 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -462,6 +462,10 @@ static const OSSL_ALGORITHM deflt_signature[] = { #ifndef OPENSSL_NO_CMAC { PROV_NAMES_CMAC, "provider=default", ossl_mac_legacy_cmac_signature_functions }, #endif +#ifndef OPENSSL_NO_SLH_DSA + { PROV_NAMES_SLH_DSA_SHA2_128S, "provider=default", ossl_slh_dsa_sha2_128s_signature_functions, + PROV_DESCS_SLH_DSA_SHA2_128S }, +#endif /* OPENSSL_NO_SLH_DSA */ { NULL, NULL, NULL } }; diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 9f0cda9311..e73fee2c8f 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -411,6 +411,9 @@ extern const OSSL_DISPATCH ossl_sm2_signature_functions[]; extern const OSSL_DISPATCH ossl_ml_dsa_44_signature_functions[]; extern const OSSL_DISPATCH ossl_ml_dsa_65_signature_functions[]; extern const OSSL_DISPATCH ossl_ml_dsa_87_signature_functions[]; +#ifndef OPENSSL_NO_SLH_DSA +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_signature_functions[]; +#endif /* OPENSSL_NO_SLH_DSA */ /* Asym Cipher */ extern const OSSL_DISPATCH ossl_rsa_asym_cipher_functions[]; diff --git a/providers/implementations/signature/build.info b/providers/implementations/signature/build.info index e082ba876e..8cb173b5b5 100644 --- a/providers/implementations/signature/build.info +++ b/providers/implementations/signature/build.info @@ -7,6 +7,7 @@ $MAC_GOAL=../../libdefault.a ../../libfips.a $RSA_GOAL=../../libdefault.a ../../libfips.a $SM2_GOAL=../../libdefault.a $ML_DSA_GOAL=../../libdefault.a ../../libfips.a +$SLH_DSA_GOAL=../../libdefault.a IF[{- !$disabled{dsa} -}] SOURCE[$DSA_GOAL]=dsa_sig.c @@ -37,3 +38,7 @@ SOURCE[$MAC_GOAL]=mac_legacy_sig.c IF[{- !$disabled{'ml-dsa'} -}] SOURCE[$ML_DSA_GOAL]=ml_dsa_sig.c ENDIF + +IF[{- !$disabled{'slh-dsa'} -}] + SOURCE[$DSA_GOAL]=slh_dsa_sig.c +ENDIF diff --git a/providers/implementations/signature/slh_dsa_sig.c b/providers/implementations/signature/slh_dsa_sig.c new file mode 100644 index 0000000000..777108a8dd --- /dev/null +++ b/providers/implementations/signature/slh_dsa_sig.c @@ -0,0 +1,184 @@ +/* + * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "internal/deprecated.h" + +#include +#include +#include +#include +#include +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "crypto/slh_dsa.h" + +#define SLH_DSA_MESSAGE_ENCODE_RAW 0 +#define SLH_DSA_MESSAGE_ENCODE_PURE 1 + +static OSSL_FUNC_signature_verify_init_fn slh_verify_init; +static OSSL_FUNC_signature_verify_fn slh_verify; +static OSSL_FUNC_signature_freectx_fn slh_freectx; +static OSSL_FUNC_signature_set_ctx_params_fn slh_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn slh_settable_ctx_params; + +typedef struct { + SLH_DSA_KEY *key; + SLH_DSA_CTX *ctx; + uint8_t context_string[SLH_DSA_MAX_CONTEXT_STRING_LEN]; + size_t context_string_len; + int msg_encode; + OSSL_LIB_CTX *libctx; + char *propq; +} PROV_SLH_DSA_CTX; + +static void slh_freectx(void *vctx) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + + OPENSSL_free(ctx->propq); + ossl_slh_dsa_ctx_free(ctx->ctx); + ossl_slh_dsa_key_free(ctx->key); + OPENSSL_free(ctx); +} + +static void *slh_newctx(void *provctx, const char *alg, const char *propq) +{ + PROV_SLH_DSA_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(PROV_SLH_DSA_CTX)); + if (ctx == NULL) + return NULL; + + ctx->libctx = PROV_LIBCTX_OF(provctx); + if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) + goto err; + ctx->ctx = ossl_slh_dsa_ctx_new(alg, ctx->libctx, ctx->propq); + if (ctx->ctx == NULL) + goto err; + ctx->msg_encode = SLH_DSA_MESSAGE_ENCODE_PURE; + + return ctx; + err: + slh_freectx(ctx); + return NULL; +} + +static int slh_signverify_init(void *vctx, void *vkey, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + SLH_DSA_KEY *key = vkey; + + if (!ossl_prov_is_running() + || ctx == NULL) + return 0; + + if (vkey == NULL && ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (key != NULL) { + if (!ossl_slh_dsa_key_type_matches(ctx->ctx, key)) + return 0; + if (!ossl_slh_dsa_key_up_ref(vkey)) + return 0; + ossl_slh_dsa_key_free(ctx->key); + ctx->key = vkey; + } + + if (!slh_set_ctx_params(ctx, params)) + return 0; + return 1; +} + +static int slh_verify_init(void *vctx, void *vkey, + const OSSL_PARAM params[]) +{ + return slh_signverify_init(vctx, vkey, params, EVP_PKEY_OP_VERIFY, + "SLH_DSA Verify Init"); +} + +static int slh_verify(void *vctx, + const unsigned char *sig, size_t siglen, + const unsigned char *msg, size_t msg_len) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + return ossl_slh_dsa_verify(ctx->ctx, ctx->key, msg, msg_len, + ctx->context_string, ctx->context_string_len, + ctx->msg_encode, sig, siglen); + + return 0; +} + +static int slh_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_SLH_DSA_CTX *pctx = (PROV_SLH_DSA_CTX *)vctx; + const OSSL_PARAM *p; + + if (pctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p != NULL) { + void *vp = pctx->context_string; + + if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->context_string), + &(pctx->context_string_len))) { + pctx->context_string_len = 0; + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->msg_encode)) + return 0; + return 1; +} + +static const OSSL_PARAM *slh_settable_ctx_params(void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, 0), + OSSL_PARAM_END + }; + + return settable_ctx_params; +} + +#define MAKE_SIGNATURE_FUNCTIONS(alg, fn) \ +static OSSL_FUNC_signature_newctx_fn slh_##fn##_newctx; \ +static void *slh_##fn##_newctx(void *provctx, const char *propq) \ +{ \ + return slh_newctx(provctx, alg, propq); \ +} \ +const OSSL_DISPATCH ossl_slh_dsa_##fn##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))slh_##fn##_newctx }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))slh_verify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))slh_verify }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))slh_freectx }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))slh_set_ctx_params },\ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))slh_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s); diff --git a/test/slh_dsa.inc b/test/slh_dsa.inc index fd2c1c3a06..ac10c7eb84 100644 --- a/test/slh_dsa.inc +++ b/test/slh_dsa.inc @@ -15,9 +15,8 @@ typedef struct SLH_DSA_ACVP_TEST_DATA_st { size_t priv_len; const unsigned char *msg; size_t msg_len; - /* sha256 digest of the signature - this reduces the footprint this file */ - const unsigned char *sig_digest; - size_t sig_digest_len; + const unsigned char *sig; + size_t sig_len; } SLH_DSA_ACVP_TEST_DATA; #define SLH_DSA_ACVP_ITEM(name, alg) { \ @@ -25,7 +24,7 @@ typedef struct SLH_DSA_ACVP_TEST_DATA_st { name##_pub, sizeof(name##_pub), \ name##_priv, sizeof(name##_priv), \ name##_msg, sizeof(name##_msg), \ - name##_sig_digest, sizeof(name##_sig_digest) } + name##_sig, sizeof(name##_sig) } /* * Test vectors from @@ -46,12 +45,132 @@ static const uint8_t slh_dsa_sha2_128s_0_pub[] = { static const uint8_t slh_dsa_sha2_128s_0_msg[] = { 0x9D, 0xDF }; -static const uint8_t slh_dsa_sha2_128s_0_sig_digest[] = { - 0xc7, 0xdf, 0xf0, 0xed, 0x25, 0x38, 0x49, 0xef, 0x51, 0x1e, 0x90, 0xbe, 0x0e, 0x2e, 0xb7, 0x71, - 0x65, 0x98, 0x91, 0x23, 0x17, 0x52, 0x9a, 0x61, 0xda, 0xe4, 0x32, 0x9b, 0xf1, 0x49, 0xef, 0x8b, +static const uint8_t slh_dsa_sha2_128s_0_sig[] = { + 0x34,0x59,0x17,0x61,0x41,0xf6,0x6f,0xb0,0xb2,0x13,0x73,0x0d,0x1a,0x4b,0x2c,0x21,0x61,0xf6,0xc7,0xca,0x88,0xa0,0xda,0x20,0xb8,0x63,0x7c,0x8b,0x4c,0xdc,0xce,0xcb,0x09,0x6c,0x8f,0xb6,0xd4,0x28,0xc8,0xa7,0x56,0xb5,0x25,0x04,0x98,0xe4,0x3b,0xe9,0x2f,0x07,0x68,0x12,0xf4,0x88,0xc5,0xcd,0x47,0x16,0x1f,0x7b,0xa9,0x94,0xe7,0xdb, + 0xc0,0xcc,0xeb,0xbb,0x01,0xd4,0x93,0x55,0xd9,0xa3,0x14,0x0d,0x0e,0xe6,0x93,0x49,0x8b,0x4b,0x0a,0xd6,0x01,0xcf,0xa8,0x2e,0x10,0xcf,0x9d,0x73,0x36,0x2c,0xc2,0x9c,0x3b,0x7c,0x18,0xa8,0x97,0x8a,0x68,0x8e,0x08,0xe0,0xd0,0x41,0x84,0xe0,0x9b,0x97,0xe7,0x5d,0x90,0xc6,0xb7,0xb4,0xd1,0x25,0x76,0xc2,0xa6,0x4c,0x42,0x6f,0x97,0x32, + 0x1d,0x1c,0x15,0xb9,0x2a,0xf3,0xbd,0x89,0xbc,0x86,0xa3,0xb5,0x95,0x06,0xf6,0xd7,0xb2,0x5e,0x19,0x21,0x4e,0xea,0x38,0x6f,0xb5,0x3c,0xb3,0x72,0x3d,0x2d,0xdb,0xdc,0x3b,0x6c,0x2a,0xf1,0x6e,0x74,0xbc,0x42,0xd1,0x1a,0xed,0xd5,0x36,0xa9,0xa1,0x3c,0x7e,0x6a,0x22,0x08,0xa9,0x54,0xb9,0x81,0x08,0x20,0x59,0x9b,0x05,0x47,0x1a,0xdc, + 0x57,0x46,0x4d,0x3a,0x58,0x45,0xaf,0xf2,0x91,0x26,0xff,0x1d,0x3c,0x1c,0x99,0x3d,0x10,0xd2,0x78,0x00,0x29,0x19,0x7f,0x20,0x77,0xa9,0xd3,0xd3,0x49,0x9e,0x6a,0x39,0xf1,0xe6,0xe7,0x01,0x55,0x93,0xee,0xdb,0x9c,0x5b,0x6b,0x39,0xd2,0xc7,0x89,0x14,0xee,0x6f,0x42,0xfd,0x77,0xdd,0xea,0x15,0x70,0xf9,0x96,0xaa,0xd3,0xf2,0xd8,0x07, + 0x60,0x3f,0xe1,0x29,0xf1,0x49,0xee,0x12,0xc5,0x98,0x65,0x0f,0xa1,0x82,0x7c,0x7a,0x1d,0xc1,0xdd,0xa4,0x6b,0x9a,0x1e,0x71,0x94,0xbc,0x8a,0x36,0x52,0x97,0xd9,0x96,0x02,0x80,0x6c,0x50,0x73,0xd0,0x95,0x32,0x6e,0xb0,0xf1,0x4a,0x4b,0x6c,0x20,0xca,0x9e,0x29,0x50,0x7c,0x32,0xab,0x8e,0xdb,0x03,0x2e,0xda,0x2d,0x1b,0x6f,0xa4,0x10, + 0xe5,0x3c,0x76,0x97,0x58,0xab,0x01,0x90,0x22,0x3c,0x10,0x8c,0xdd,0x19,0x6b,0x2d,0x39,0xfd,0x19,0x3d,0x96,0x0a,0x34,0x98,0x22,0x16,0xf5,0x0a,0xf0,0xfe,0xb9,0x2b,0xa1,0x35,0xbc,0xf7,0x20,0xbd,0x35,0x08,0xbc,0x0a,0x24,0xaf,0xea,0x7c,0x95,0xf2,0x41,0x6f,0x4e,0xcb,0xda,0xc2,0xe8,0x4e,0x38,0x02,0xeb,0xb7,0xbd,0x64,0xac,0x5b, + 0x2d,0x8c,0xdd,0x93,0xc7,0x45,0x5b,0x30,0x1f,0x92,0x27,0x3a,0x23,0x61,0xd7,0xf9,0x50,0xca,0xac,0xe0,0x99,0x05,0x56,0x07,0x2d,0x40,0x34,0x82,0xb0,0x4f,0x78,0xfa,0xf9,0x10,0xcd,0xd2,0x2f,0xb7,0x3d,0x98,0x62,0x81,0x54,0xe4,0x54,0xe5,0x8d,0xaf,0xb3,0x62,0xd4,0xeb,0xc0,0x7a,0x25,0xfe,0xeb,0x12,0x0f,0xfc,0x2b,0x8f,0xb3,0x46, + 0xae,0x25,0xe9,0x7c,0xa5,0xff,0x02,0xfc,0x09,0xfe,0x52,0x0d,0x88,0x9a,0x4d,0xdc,0x6c,0x6d,0x80,0x03,0x99,0xb3,0xe7,0xf3,0xf9,0xdf,0x67,0xd0,0x21,0xf9,0x88,0x28,0x95,0x6f,0xf2,0xe7,0xa8,0xea,0x80,0x5a,0xe0,0x7a,0x70,0xed,0xba,0xcb,0xe0,0xd0,0x81,0x28,0x9e,0x89,0x29,0xb3,0x0b,0x57,0xf7,0x13,0xaa,0x37,0x50,0xc5,0x47,0x1f, + 0x62,0x4d,0xde,0x13,0x32,0x1f,0x46,0xa1,0xcd,0xac,0xc6,0x54,0x53,0x92,0xc9,0x87,0x55,0x7d,0x06,0x23,0x67,0x67,0xf8,0xb7,0xb1,0x71,0x66,0x16,0xb8,0xdb,0x26,0x9c,0xab,0x8f,0xda,0x80,0x45,0xd8,0xa4,0x24,0x40,0x58,0xb3,0xaf,0x3c,0xe4,0xf9,0x6c,0x48,0xbd,0x4f,0xa2,0x0f,0xa7,0x30,0x7c,0xcd,0x0a,0x54,0x56,0xca,0x89,0xbb,0x2d, + 0xfd,0x92,0x09,0xec,0xfd,0x2c,0x14,0xcb,0xa3,0x74,0x28,0x6f,0x46,0xa8,0x5b,0xfc,0x59,0xa3,0xe0,0x6c,0xf1,0x61,0x5c,0x96,0x2d,0x40,0x79,0x2b,0x83,0xda,0xd3,0xfa,0xa7,0xb8,0xad,0xed,0x0b,0x04,0xa1,0x75,0xa3,0x70,0x53,0x82,0x08,0xf8,0x97,0x5b,0x0a,0x9c,0xa4,0xca,0x8c,0xce,0x90,0xd3,0x58,0xf9,0xa3,0x8d,0xa8,0xde,0x2b,0x35, + 0x3a,0x41,0xa3,0xcd,0xf2,0x75,0xbd,0xb7,0x2a,0xa0,0x88,0xff,0xdb,0x1a,0x06,0xd5,0xc5,0x66,0xdd,0x5e,0x87,0x20,0xef,0x51,0xc1,0x33,0xee,0x66,0x60,0x7a,0xca,0x53,0xf0,0x88,0xec,0xb4,0xdc,0xa8,0x18,0xd2,0x69,0xd0,0x71,0x5c,0x7a,0xf2,0x89,0xee,0x4c,0x03,0x26,0xa0,0x67,0xe0,0x1d,0x4d,0x76,0x35,0x23,0x39,0xbd,0x36,0x17,0x52, + 0x19,0x38,0xf6,0x8f,0x11,0x2d,0xe0,0x85,0x7b,0x23,0x65,0x3a,0xe4,0x20,0xa6,0xe5,0x3a,0x13,0x0c,0xe9,0x56,0x3c,0x99,0x55,0x2f,0x05,0x8d,0x18,0xec,0x51,0xa4,0xb4,0xc2,0x66,0x15,0x1f,0x89,0x7e,0xa0,0x68,0x6e,0x4d,0x32,0xd4,0x56,0x41,0xfa,0x99,0xca,0x87,0xfc,0x98,0xfd,0x68,0x8a,0x27,0x47,0xf6,0x74,0x57,0x07,0xe8,0xca,0xd1, + 0x5c,0x06,0xa5,0x74,0x43,0xac,0xa8,0x24,0xd4,0x45,0x17,0x2f,0xbe,0x09,0xcc,0x05,0x99,0x10,0x56,0x8a,0x18,0xf2,0xae,0xcf,0xf2,0x63,0xe1,0xdb,0x44,0xe6,0xa1,0x95,0x04,0xe6,0xa7,0x44,0x54,0x59,0xde,0x58,0xeb,0x12,0xb7,0xe0,0x51,0x1c,0x4a,0xd0,0xe2,0x4e,0xc9,0x8d,0xc3,0x5b,0x06,0x01,0xbe,0x24,0xde,0xd8,0x21,0x1d,0xc0,0xeb, + 0x87,0x75,0x3b,0x12,0x0d,0x13,0xfc,0xf4,0x29,0x47,0x68,0x5d,0x42,0x17,0x86,0xd4,0xa8,0x01,0x91,0x95,0x8a,0xf4,0xdb,0x3c,0x12,0x5d,0x4e,0x32,0x5e,0x1a,0xe8,0x56,0xea,0xea,0xf1,0x13,0x41,0x4d,0xc0,0x07,0x4e,0x6b,0xc5,0x24,0x46,0xd6,0x51,0x27,0x09,0x2b,0x2e,0x98,0xb3,0xdd,0xe9,0x05,0x4b,0xed,0xad,0x30,0x30,0xba,0x43,0x24, + 0x55,0xba,0xc9,0x49,0xf3,0x0c,0x5b,0x6e,0xc3,0x13,0x77,0xdb,0x5c,0xa4,0x02,0x71,0xcc,0x94,0x0a,0x70,0x7c,0xfb,0x65,0x43,0x6c,0x33,0x23,0xc6,0x45,0x28,0x92,0x0e,0x95,0x05,0x76,0x3c,0x96,0xf4,0xe2,0xbc,0x41,0xbf,0xdd,0x1d,0x68,0x71,0xdb,0xd0,0xc0,0x12,0x8f,0xfb,0x34,0x69,0x00,0x9a,0x72,0x67,0xc6,0x1f,0x23,0x3c,0x17,0x75, + 0xe7,0x44,0x39,0x30,0xff,0x04,0xde,0x18,0x0c,0x5e,0xb1,0x97,0xd0,0xc4,0xee,0xd5,0x94,0xb8,0x64,0x56,0x88,0xba,0xf1,0xba,0xad,0xa8,0xdc,0xbb,0x20,0x67,0xe0,0x4a,0x84,0x3a,0x67,0x4a,0x3f,0x3f,0x75,0xc3,0x11,0xb6,0xa1,0x90,0x97,0x66,0x70,0x70,0x9d,0x83,0xf3,0xcd,0xa5,0xaf,0x1d,0x50,0x0f,0x1f,0x13,0x8d,0xbf,0xc5,0x88,0x44, + 0x8a,0x07,0x6c,0xc5,0xbf,0xc6,0x9e,0xeb,0xbf,0xe6,0x2d,0x90,0xa3,0x30,0xa9,0x49,0xeb,0x0d,0xd0,0x87,0xc0,0xfa,0x9e,0x9c,0x47,0xd7,0x4c,0xe4,0x07,0x68,0x03,0xee,0x2a,0xc8,0xa4,0x85,0xa7,0x2e,0xef,0x73,0x3b,0x9b,0xaa,0x92,0x48,0x1b,0x1f,0x69,0x0e,0xc4,0xef,0xae,0x6e,0xa9,0x1f,0x64,0x55,0xab,0x26,0x5c,0x3d,0xe4,0xb1,0x98, + 0xad,0x26,0x9d,0x73,0xbf,0x97,0x31,0x38,0x18,0xd0,0x2b,0xe3,0xfb,0x78,0x14,0x6b,0x7c,0xaf,0x89,0x2b,0xa2,0x95,0xda,0xa2,0x04,0x96,0x70,0x91,0x4d,0xbf,0x60,0x8b,0x1c,0x48,0x85,0x58,0x83,0xca,0xd1,0x3e,0x0a,0x0e,0xd6,0x8e,0xdb,0x48,0xff,0xb2,0x0d,0xee,0xef,0x2b,0x1c,0x0e,0x41,0xf0,0x82,0x22,0xc7,0x32,0x32,0x51,0xe9,0x02, + 0xd3,0x6b,0xd0,0x50,0x63,0x15,0x64,0xd5,0xc2,0x0e,0xe3,0x64,0x8d,0xe1,0xf7,0x9e,0xc2,0x6e,0x35,0xd9,0x5a,0x41,0x57,0x3d,0x52,0xc4,0x15,0x4b,0xac,0x38,0x2d,0x9c,0x60,0xff,0x52,0xb3,0x27,0xc5,0x0b,0xde,0xfd,0x7f,0x44,0xd9,0x65,0xab,0xcd,0xa0,0x83,0x59,0xd3,0xe5,0x03,0xeb,0x34,0x90,0xba,0x0f,0x00,0x37,0x9f,0x68,0x03,0x4b, + 0x82,0x2e,0x02,0x7c,0xa8,0x5d,0x97,0x91,0x86,0xe5,0x7c,0xe5,0x26,0x0b,0xc3,0x49,0x3c,0xa3,0x53,0x3a,0xa2,0x37,0xd3,0x0c,0xeb,0xa6,0x1d,0x47,0x6c,0x4e,0xba,0x25,0x47,0xb6,0x7a,0x94,0x6a,0x3c,0x82,0xe9,0xa6,0xc5,0x0f,0xfb,0x64,0x33,0xfa,0x6e,0x0c,0x52,0x94,0x21,0x02,0x77,0xe9,0xa5,0x17,0x3f,0xae,0x63,0xb0,0xb6,0x3f,0x1d, + 0xde,0x79,0x95,0x30,0x20,0x3f,0x49,0x80,0xb3,0x28,0xcf,0xc6,0xb3,0x18,0xb7,0x57,0xb6,0xa7,0x8e,0x71,0xf5,0x67,0x88,0x9e,0xd1,0xa1,0x1d,0x6d,0xa2,0xfd,0x5b,0xff,0x6b,0x1e,0x07,0x85,0x7b,0xca,0x99,0x36,0xf4,0xe1,0xf7,0x79,0x77,0x31,0x8c,0x18,0xe6,0x9c,0xba,0xbe,0xd2,0x49,0xe3,0x86,0x14,0x9c,0x57,0x96,0xbf,0x34,0x06,0x2a, + 0x5b,0xa2,0x03,0x8a,0x1d,0x4e,0xa3,0xb3,0x76,0xd1,0x2e,0x82,0x16,0xd8,0xec,0x09,0xf7,0xa2,0x78,0x74,0x32,0x67,0x06,0x8b,0x58,0x87,0x0a,0x72,0xff,0xe0,0xe5,0xb7,0x0d,0x77,0xc8,0xf2,0x74,0x34,0x1b,0xde,0x14,0x06,0x3b,0xcc,0x15,0x64,0x23,0x25,0xec,0x8e,0x2b,0x1d,0x08,0x97,0x03,0x87,0xde,0x8a,0x4b,0x86,0x82,0x20,0x70,0x6b, + 0x4b,0x9c,0x3d,0x55,0x8d,0xbc,0x38,0xc3,0x34,0xbe,0x8e,0xa3,0x35,0xb5,0x37,0x2c,0x26,0x21,0x63,0xae,0x2d,0xc8,0x88,0x7f,0xf6,0xcb,0xc6,0x0a,0x93,0x4f,0xfd,0x72,0x85,0x97,0xdd,0x70,0xac,0xbd,0x31,0xb5,0xe5,0x90,0xb5,0x23,0xa1,0x7c,0xbb,0x0c,0x66,0x40,0x56,0xea,0x41,0x2f,0x19,0xc4,0x39,0x68,0x8f,0xf8,0xb0,0xf3,0xc5,0x4e, + 0xd4,0xf7,0xd2,0x07,0x74,0x05,0xe4,0xa5,0x2f,0x33,0x8d,0x75,0xa8,0x7a,0xa3,0x5a,0xcf,0x2d,0x3a,0x0f,0xbf,0xe2,0x3c,0x48,0x97,0x6b,0x78,0x65,0xbc,0x76,0xb3,0xb2,0x27,0x4d,0x1c,0xb5,0x20,0x71,0xa9,0x99,0xa1,0x4b,0xb2,0xaf,0x21,0xd8,0x8b,0xde,0xac,0x88,0x75,0x79,0x2a,0xb0,0x8c,0x89,0xa7,0x5e,0x30,0x62,0xec,0x26,0xa1,0x32, + 0xec,0xe5,0x4e,0x62,0xfc,0x3d,0x57,0x89,0x80,0xdb,0x3c,0x1b,0xda,0x89,0x9f,0x21,0xf1,0x6b,0x18,0xde,0x46,0xba,0x45,0xbf,0x63,0xad,0x7f,0x08,0xf2,0xa8,0x81,0x1a,0xe8,0x5f,0xe6,0xa8,0xbc,0xb1,0xfe,0x84,0x21,0x95,0x4c,0xc4,0x12,0x60,0x1e,0x92,0x20,0x14,0x09,0x0c,0xda,0xcf,0x00,0xad,0x38,0x86,0x1c,0x3f,0xb8,0x5f,0x39,0x31, + 0xcb,0xa7,0xbd,0x8a,0xe3,0x30,0x2d,0x44,0x82,0x05,0x09,0xc7,0x5d,0xa9,0x80,0x32,0x7b,0x0e,0xd8,0xdc,0xc3,0x85,0x92,0x82,0xf1,0x33,0xb3,0x30,0x0a,0x6a,0xbb,0x05,0x12,0xd6,0x0e,0x7a,0x6c,0xf9,0x4a,0x19,0x43,0xdc,0x4b,0x02,0xeb,0xe8,0x6b,0x42,0x74,0x71,0xae,0xf5,0x1c,0xe5,0x6f,0x61,0xcb,0x1a,0x1f,0x18,0x5c,0x06,0xec,0x16, + 0x6b,0x5e,0x4e,0x65,0x37,0x95,0x1b,0xaa,0xa2,0x9e,0xd6,0x76,0x6f,0x34,0xa4,0xc1,0x41,0x8f,0x81,0x66,0x05,0x79,0xd1,0x93,0xe5,0xc6,0xf2,0xc2,0xcd,0xa0,0x9f,0x62,0xb6,0x3b,0x87,0x8d,0x65,0x8a,0x14,0x05,0xd5,0x75,0xe6,0x4c,0x98,0x6f,0x9c,0x37,0x67,0xc7,0x7f,0x9d,0x15,0x11,0x34,0x84,0x0a,0x67,0xc0,0x9a,0x62,0xfe,0x31,0x36, + 0x6b,0x9d,0x30,0xa7,0x98,0x9d,0x32,0xab,0xe8,0xb5,0xeb,0xa1,0x30,0x39,0x0e,0x60,0xad,0xc0,0xf0,0xd4,0xea,0x51,0x3c,0xd4,0xe0,0xae,0x1d,0x30,0xe4,0x1d,0x4f,0xb8,0x4f,0xba,0x92,0x24,0xca,0xcd,0x58,0x94,0xd7,0xd6,0x75,0x74,0xf9,0x15,0xf4,0xe9,0x0c,0x86,0x4e,0xbe,0x98,0x98,0xa6,0x96,0xf0,0x3e,0xf2,0x2b,0x49,0xa5,0xb1,0x2a, + 0x73,0x91,0x3f,0x13,0x5d,0xda,0xc5,0xda,0x04,0xbd,0xc8,0x21,0xe7,0xea,0xe6,0xdd,0x97,0xa0,0x8e,0xc8,0x4a,0x4c,0xe6,0xac,0xaa,0xcd,0x41,0x72,0xe7,0x45,0x53,0xa2,0x21,0x34,0xeb,0x1d,0x1f,0xa9,0x2a,0x9a,0xaa,0x25,0xc6,0x3b,0x9c,0xf1,0xec,0x3a,0xb8,0x24,0x08,0xf0,0xe8,0x6e,0x17,0xbe,0xf0,0x89,0xea,0x5a,0x1f,0x36,0x2e,0x9b, + 0xcb,0x79,0x8d,0x21,0x02,0x9c,0x7f,0x9b,0xb4,0xa4,0xfb,0xec,0x87,0x20,0x26,0xb4,0x17,0x38,0x68,0xd0,0xf2,0xe9,0x50,0xab,0xc3,0x4d,0xe6,0xe6,0xad,0xf6,0x74,0xa5,0x22,0xbc,0xa1,0x89,0x93,0x54,0xbc,0x6b,0xd9,0x71,0x4d,0x88,0xf8,0xac,0x76,0xb1,0x7e,0xb7,0xfc,0x25,0xc6,0xc1,0x0f,0xb2,0xbf,0x2d,0xa7,0xea,0x10,0xc2,0x7b,0x2b, + 0x81,0xd5,0x59,0xbe,0x83,0xcb,0xed,0xd6,0xb3,0x9c,0x28,0x3f,0xde,0x27,0x26,0x97,0xf5,0x54,0xde,0x73,0x55,0xd9,0xd0,0x90,0x50,0x45,0x14,0xe3,0xbb,0x24,0x0f,0x21,0x10,0x54,0x21,0xa4,0x4b,0x6f,0xf4,0x81,0x94,0x23,0xaa,0xb3,0xa7,0xd6,0x24,0x43,0x39,0x00,0x04,0xcf,0x39,0xfa,0x3e,0x10,0xfe,0xb5,0xd1,0x5d,0x3a,0xae,0x0d,0xf3, + 0xe7,0x4e,0x27,0xc3,0xba,0x8b,0x3d,0x22,0x6a,0x61,0xc5,0xcc,0xe3,0x8b,0x33,0xe7,0xb0,0xaf,0xe7,0x4c,0x71,0xee,0x01,0xa3,0x14,0x46,0x34,0x35,0xcb,0xe8,0xdb,0xc5,0x03,0xb8,0xd8,0xa1,0x46,0x53,0xe8,0xdd,0x36,0x75,0x01,0x5a,0x76,0xe2,0x6a,0xbd,0xf3,0x07,0x10,0x9f,0x31,0xe5,0x54,0x73,0x48,0x15,0xf1,0xa2,0xc5,0x1c,0x8b,0x38, + 0x0e,0x6b,0x4b,0x70,0x5f,0x87,0xd4,0x30,0x08,0x5f,0x56,0x5b,0x5f,0x77,0x03,0x98,0x5e,0xcd,0x70,0x10,0x85,0xc8,0xb5,0x0a,0x3a,0x5b,0x20,0xc3,0x63,0x6f,0x79,0x63,0x69,0x2e,0x36,0x59,0xf2,0x6e,0x32,0x07,0x38,0xd2,0x0c,0xe7,0xf8,0x39,0xdd,0x14,0x43,0x3e,0xd8,0x51,0xd5,0xc1,0x2c,0x88,0x68,0x08,0x73,0xe5,0xf5,0x1c,0x85,0x56, + 0x70,0x0a,0xcd,0xba,0xeb,0x30,0xe5,0xb3,0x29,0x7c,0xf6,0x38,0xb6,0x26,0x48,0xaa,0x1c,0x6e,0x1b,0x34,0xb6,0xe9,0x0f,0xac,0x92,0xa7,0x97,0x17,0x19,0xf9,0x59,0x80,0xc6,0x3a,0x77,0x57,0xc3,0x3c,0x60,0xca,0xfb,0xf3,0x2d,0x50,0xea,0x31,0xa3,0xdf,0x69,0xa7,0xcf,0xf3,0x20,0x13,0xa2,0xab,0x5d,0xba,0xd4,0x72,0xbc,0x78,0x9b,0x51, + 0x79,0xa7,0x6e,0x5a,0xca,0x1a,0x96,0xf9,0x14,0x8d,0x50,0xc6,0x95,0xa8,0x7b,0xc8,0x10,0x87,0x0a,0xaf,0xb2,0x5b,0xa8,0xff,0x11,0x6c,0x52,0x53,0x28,0x7c,0x96,0x9f,0x96,0x5f,0xe3,0xbb,0xaf,0xc3,0x13,0x7e,0xc9,0xf9,0x70,0x08,0x1d,0x68,0x9c,0xda,0x53,0xc8,0x14,0x56,0x48,0x77,0x9f,0x47,0x74,0x02,0x3a,0x1d,0x48,0x6d,0xa2,0x41, + 0x4a,0x64,0x2b,0x97,0x6e,0x7c,0xa5,0x11,0x40,0xb6,0x7b,0x70,0xea,0x22,0xf3,0xa9,0x44,0xc6,0x72,0x7b,0x2b,0x70,0xab,0x78,0x82,0x75,0x26,0x4f,0x71,0xb7,0x84,0x01,0x61,0xab,0x60,0xfe,0x06,0x9a,0xe3,0x99,0xce,0x6e,0x39,0xf0,0xba,0x8b,0x28,0x5c,0x38,0xdf,0xcf,0x62,0x23,0x6a,0x40,0x67,0x12,0x6e,0x74,0xbf,0x70,0xad,0x2d,0x40, + 0x83,0x13,0xf3,0x90,0x7b,0xc7,0xcf,0x02,0xf1,0xba,0xc4,0x04,0x92,0xea,0x98,0xac,0xf7,0xe9,0xc9,0xab,0xc8,0xca,0x82,0xe1,0x09,0xd6,0x39,0x78,0xc6,0x38,0xbc,0xb7,0x7a,0x87,0x8b,0xc1,0xea,0x17,0x14,0x1b,0xa5,0x18,0xb3,0x17,0x44,0x7d,0x2b,0xa5,0xa3,0x3e,0xf9,0xf7,0x61,0xf7,0x37,0x3a,0xb1,0x5d,0x80,0x6b,0xb0,0xca,0x03,0xd4, + 0x5f,0xaf,0x63,0x39,0x10,0x86,0xfe,0x0c,0x48,0x5d,0x78,0xef,0x65,0x7a,0x67,0x9d,0x06,0xdf,0x29,0x65,0x59,0x11,0x2a,0x76,0xde,0xba,0x2e,0xdc,0xe8,0x83,0x39,0xfb,0xc4,0x61,0x01,0x24,0x80,0x66,0xe1,0x8c,0x4d,0x7c,0x58,0x0d,0x4c,0x9b,0x02,0x07,0xa7,0x9b,0x73,0x8f,0xba,0x66,0xe3,0xba,0x76,0xc4,0xa7,0x08,0xf5,0x1e,0x5e,0x6d, + 0x7f,0x99,0x2e,0xd9,0xf6,0xce,0x63,0x89,0xf0,0xd0,0x15,0xdb,0x4c,0x16,0xd5,0x04,0xf6,0x0e,0xae,0x82,0xb7,0x24,0x56,0x1a,0x1b,0x6e,0x8a,0x8d,0x70,0x0b,0xda,0xfc,0xe5,0x6b,0xe1,0xc6,0xd7,0xbd,0xc5,0xd1,0x96,0x7c,0x54,0x58,0xb5,0xa3,0x01,0x44,0xa6,0x43,0x43,0xc2,0x52,0xd8,0x11,0x33,0xe0,0xaa,0x20,0x06,0x8c,0xe0,0xfb,0x77, + 0xb5,0xab,0x28,0x30,0x55,0xda,0xf6,0xed,0xcd,0x68,0xca,0x53,0x1d,0x4e,0x41,0xb2,0x36,0x33,0xb4,0x92,0xe7,0x93,0x64,0xa0,0xed,0x3c,0xa3,0x20,0x95,0xc4,0x56,0x84,0x0d,0x6c,0x02,0xa1,0x22,0x27,0x57,0x21,0x27,0xb4,0xd6,0xe4,0xd3,0x10,0x7f,0x37,0x0a,0x64,0x78,0x9f,0x60,0xaa,0x04,0xf7,0x1f,0x30,0xc8,0xe5,0x37,0x56,0xc8,0x99, + 0x9d,0x3c,0x67,0x90,0xee,0xaa,0xb3,0x36,0xf5,0xba,0x7d,0x82,0xe8,0xa4,0x33,0xfa,0x74,0x70,0xdb,0x78,0xe5,0xea,0xd4,0x0e,0xec,0xfd,0xbc,0xc3,0x87,0xe5,0x44,0xd0,0x66,0x96,0x89,0xfa,0xfe,0x37,0x01,0xef,0x90,0x80,0xec,0x6d,0x0b,0x80,0x91,0x25,0x06,0xe7,0x1f,0x43,0xc0,0x14,0x73,0x96,0xd9,0xcc,0x70,0x83,0x54,0x06,0xf2,0x7b, + 0xd3,0x86,0xd9,0x7c,0xd8,0xb7,0x4a,0x51,0xad,0xac,0x28,0x88,0x5c,0xdf,0xda,0x75,0x50,0x97,0x16,0x9c,0x5e,0xd3,0x81,0xfd,0x34,0x70,0xba,0x0a,0x31,0x48,0xdf,0x53,0xcc,0xb4,0x02,0x2a,0x41,0x46,0xbf,0x19,0xf6,0x10,0x6c,0x49,0x1c,0xc0,0x35,0x36,0x17,0x9e,0x50,0x62,0xba,0x01,0x8f,0xee,0x98,0x34,0xaf,0x53,0x41,0xc7,0xc0,0x7d, + 0x4f,0xa5,0xc0,0x4c,0x7b,0x14,0xab,0xa5,0x16,0xf0,0xec,0xd8,0x5a,0xca,0xe8,0xd8,0x75,0xa7,0xd4,0x28,0x09,0x5a,0x57,0xf7,0x62,0xe2,0x8e,0x21,0x4d,0xb7,0x7e,0x16,0x59,0xc5,0xba,0x99,0x34,0x97,0x08,0xfc,0x4f,0x5d,0x5c,0x08,0xe4,0x96,0x49,0x9b,0xec,0x4c,0x56,0xde,0x78,0xe6,0xf6,0x35,0x4a,0x4c,0x8c,0x34,0xf7,0x4c,0x36,0xff, + 0x4c,0xb0,0xaa,0xa3,0x01,0x3b,0x2a,0x90,0xf7,0xfc,0x62,0xe9,0xbf,0x3a,0x34,0x6d,0x07,0x55,0x38,0xf5,0x89,0x36,0x08,0x35,0xad,0xca,0xe0,0x1d,0x5f,0x54,0xf1,0x75,0x43,0x2a,0xaa,0xa8,0xf2,0x49,0x9b,0x81,0xee,0x95,0xa6,0x27,0x02,0x2e,0xd5,0x1b,0x43,0xa0,0xaa,0x3c,0x57,0x52,0xc5,0xd2,0xdc,0x48,0x23,0xff,0x8a,0x5c,0x1f,0x6d, + 0xb8,0xc6,0xbd,0x7e,0xf7,0x08,0x52,0xda,0x54,0x75,0x5b,0x99,0x06,0x7b,0x8d,0x7f,0x52,0xaf,0x09,0x9d,0xb4,0x93,0x46,0xf2,0x6b,0x07,0xce,0xc0,0xad,0x09,0xfc,0x09,0x14,0x99,0xcf,0xdc,0x9c,0x65,0x4b,0x7f,0x89,0xad,0xef,0x1b,0x97,0x71,0x01,0xf7,0x63,0xdf,0x54,0x7a,0xd9,0xe9,0xc5,0x18,0x7b,0xff,0x93,0xd8,0x83,0xf1,0x3c,0x42, + 0x6c,0x6f,0x21,0x5d,0x99,0x57,0xe4,0x07,0xac,0x72,0x9f,0xad,0x4c,0xe6,0x18,0x02,0xfb,0xfd,0x99,0xc7,0xc4,0x91,0x05,0xc6,0x89,0x46,0x68,0xce,0xa8,0x5c,0x41,0x62,0x43,0x02,0xf3,0xa4,0x27,0xb0,0x98,0xcd,0x12,0x07,0x64,0x60,0x51,0xf4,0xc6,0x76,0x31,0xe3,0x25,0x4f,0xc2,0x74,0xce,0x07,0x19,0xf5,0x9b,0x4b,0xa4,0xdd,0xe4,0xd4, + 0x16,0x21,0x9a,0x0d,0xe4,0x83,0x86,0xe1,0x92,0xde,0x5d,0x1b,0xd0,0x98,0x15,0x3e,0x75,0x3e,0x81,0xf2,0xf8,0x18,0xba,0xc2,0x8a,0x99,0x8d,0xd3,0x35,0xb9,0xd1,0x66,0x7a,0xe7,0xe9,0x1d,0x8d,0x06,0xf2,0xac,0x78,0x55,0x66,0xa3,0x91,0x40,0x78,0x70,0x04,0xe3,0x67,0xec,0xea,0x54,0x4a,0x03,0x0a,0x01,0x76,0xb5,0x68,0xe3,0x93,0xf7, + 0xca,0x95,0x65,0xb8,0x69,0x0c,0x53,0xb9,0x77,0x0f,0x37,0xd8,0x43,0xb5,0x36,0xc4,0x4e,0xce,0xf1,0x5a,0xb0,0xe7,0xfa,0x31,0x43,0x43,0x68,0xb3,0xec,0x08,0xee,0x50,0xb1,0x64,0xfb,0x6f,0xca,0x61,0x8a,0xcf,0x2d,0x4d,0x8c,0x21,0x4d,0x86,0x14,0x8b,0xfd,0x2d,0x1c,0x05,0x6f,0x21,0xbd,0xb0,0x6d,0x1a,0xc2,0x71,0x11,0x20,0xcc,0x3d, + 0x85,0xea,0x84,0x37,0x24,0x65,0x9f,0xcf,0xf4,0xf2,0x48,0xa5,0x91,0x51,0xe7,0xc5,0x4b,0x9d,0xbc,0x82,0x06,0x66,0x5c,0x8c,0xeb,0xfe,0x58,0x1a,0x05,0x77,0x16,0x21,0x93,0x78,0x19,0x1e,0x9a,0x4d,0x38,0xde,0xda,0x19,0x80,0x6d,0x88,0x2c,0x91,0xdd,0x1d,0x27,0x4a,0xe0,0xe6,0xf7,0x9c,0x4e,0x26,0x47,0xa1,0x28,0xb0,0xa8,0xc3,0x15, + 0x45,0x7a,0x96,0x6d,0x45,0x77,0x7c,0xbb,0x08,0x24,0xb1,0x46,0x00,0x2f,0xc8,0xa1,0xe6,0x2e,0x56,0xff,0x7a,0x02,0x26,0xea,0xc3,0x6f,0x8f,0x30,0x00,0x4a,0x98,0x0b,0x61,0x65,0x75,0xce,0x67,0x38,0x26,0x3e,0xc5,0x08,0xc1,0x29,0x14,0x31,0xfa,0xb6,0x97,0xba,0x43,0x20,0x48,0xe9,0xe3,0x0f,0xa2,0x7d,0x36,0xc8,0x1f,0x5a,0x12,0xf5, + 0xcd,0x84,0x2d,0xd9,0xc3,0xdb,0x54,0x5b,0x6d,0x28,0x7e,0xe4,0x08,0x99,0x55,0x5b,0xe2,0x94,0x49,0xb2,0xdd,0x7a,0x92,0xbc,0xe8,0xb8,0xeb,0x1c,0x7d,0xa7,0x38,0xb6,0xf7,0x7f,0xa8,0xd7,0x54,0xe0,0x4c,0x50,0xc0,0xc8,0x4b,0xfb,0x43,0x3e,0x25,0xa4,0x08,0xe4,0xf0,0x45,0x53,0xf1,0xde,0xca,0x74,0xca,0xc9,0x9e,0x0b,0x5a,0xf8,0x08, + 0x19,0x1e,0x4c,0x92,0x14,0xed,0x04,0x52,0xb8,0x20,0x60,0xe6,0xd8,0x3e,0xca,0x3a,0x91,0xf5,0x51,0xa3,0x74,0xbf,0x88,0xd7,0x8c,0x4a,0xdc,0x11,0xd0,0xc2,0x3d,0x57,0x1c,0x9d,0x43,0xca,0xfd,0x21,0x99,0x2a,0xf2,0xd6,0xc8,0xa9,0xdf,0xde,0x2e,0x12,0xae,0x2a,0x21,0x16,0x61,0x10,0x7b,0x0e,0xea,0x1e,0xe1,0xad,0xab,0xd6,0xf0,0x91, + 0x21,0x7d,0xa7,0x79,0xad,0xfa,0x19,0x08,0x82,0x82,0xd8,0x6f,0x2b,0x90,0xcf,0x26,0x35,0xa7,0xb4,0x32,0x53,0xe8,0xc3,0x61,0x3a,0x43,0xd9,0x06,0x87,0x6a,0xda,0xfe,0xfc,0xfc,0x2e,0x41,0x0c,0x42,0x6b,0xa8,0xf9,0x32,0xc8,0x62,0x0d,0x94,0xe3,0xbf,0xdf,0xe8,0xec,0x40,0x7d,0xc0,0x12,0x14,0xb0,0x8a,0xd7,0x2b,0x4f,0x3c,0x98,0x29, + 0xef,0xd2,0x39,0xa7,0x8f,0xca,0x98,0x7c,0x73,0xd1,0x3e,0x12,0x0d,0x17,0xbb,0x93,0x0e,0x1e,0x55,0xa6,0xe9,0x73,0xee,0x00,0x68,0x3f,0x1c,0x8b,0x9d,0x1a,0x99,0x1f,0x62,0x27,0xa3,0x3c,0xfa,0xce,0x97,0xe2,0x91,0xc4,0xf7,0xc3,0x9e,0x81,0xef,0xad,0x26,0x53,0xf4,0xfc,0x0b,0x9d,0x2a,0x47,0x3b,0xfa,0x8c,0x3d,0x0f,0x9c,0xe6,0xdb, + 0x9b,0xae,0x7a,0xe5,0x7a,0xe7,0x12,0xf5,0x0b,0xc6,0xbb,0x99,0x48,0xfd,0xf1,0x8b,0xf3,0xcc,0x96,0xc0,0x7f,0x82,0x82,0x08,0xda,0x17,0x4b,0xe3,0x0b,0xd9,0x8f,0x8f,0xd8,0x45,0xef,0x42,0x8d,0x39,0xcc,0xbc,0xae,0xb8,0x1e,0x40,0xaf,0x10,0xd6,0x25,0x3c,0x43,0x02,0xcf,0x50,0xa7,0xa2,0x5d,0x58,0x58,0xb3,0x30,0x24,0xc5,0x7c,0xe3, + 0x66,0x49,0x78,0x91,0x0f,0x2e,0x3f,0x00,0x8a,0x65,0x60,0x07,0xe7,0xd5,0x66,0xe3,0x24,0xe1,0x0e,0x93,0x26,0xae,0xf9,0xd1,0xa5,0x8c,0x69,0xd0,0x61,0x7b,0x6b,0x7f,0xa7,0x2b,0x6d,0x86,0x5c,0x65,0x0a,0x8d,0xbc,0x0c,0xb0,0x47,0xed,0x2d,0xdf,0x14,0x6e,0xd0,0x78,0xe5,0xa7,0xb5,0xe7,0x9b,0xb4,0x30,0x0d,0x69,0xb4,0x2e,0x70,0xa3, + 0x64,0x19,0x28,0x75,0x8c,0x40,0x06,0x4a,0x2d,0x07,0x23,0x66,0xd1,0x44,0xc5,0x13,0xdd,0x62,0x60,0x5c,0xf6,0x9d,0xe8,0x61,0x12,0xea,0x83,0xbb,0xea,0x14,0x9f,0x4b,0x19,0x0e,0x24,0xb3,0x3f,0xf2,0xa4,0x19,0xec,0xd7,0x78,0xad,0xe3,0x17,0x8d,0xfa,0xe1,0xa1,0x93,0xd1,0x1a,0x61,0x6f,0xdc,0x84,0xf2,0xba,0x9d,0xb0,0xf0,0x91,0x43, + 0xa3,0x40,0xe4,0xda,0x64,0xc8,0x18,0x3b,0x0a,0x82,0xba,0x23,0xdc,0x06,0x1f,0x08,0xba,0xc7,0x5c,0x2a,0x3b,0x68,0xa7,0xf8,0x6c,0x81,0xca,0xa6,0x33,0xfe,0x46,0xa2,0x09,0x75,0x96,0xed,0x7e,0x35,0xa4,0x0b,0x39,0x7c,0x27,0xfe,0xe1,0x40,0x9a,0x2b,0xce,0x81,0x27,0xae,0xe2,0xbf,0xb9,0xbe,0xc1,0xb5,0x28,0xdf,0x8a,0x16,0x7f,0x28, + 0x73,0x93,0xe7,0xd4,0xc8,0x5e,0x6a,0x8e,0xdd,0xd4,0x39,0xad,0x5d,0xc9,0x6f,0x5e,0xc9,0xaf,0x43,0x19,0x79,0xe0,0x4a,0x18,0x6c,0xce,0xd9,0xc5,0x60,0x3e,0xb9,0xb6,0x7a,0xb4,0x7a,0x83,0x4b,0x3c,0x1e,0x78,0x76,0x87,0x1e,0x52,0xde,0x37,0x81,0x6f,0x88,0x97,0xbe,0xd4,0x82,0x0d,0x15,0xe9,0xcb,0x23,0xc1,0xd5,0xe3,0x81,0x6f,0xcf, + 0x21,0x0b,0x78,0xa8,0xb3,0x8b,0x17,0xe4,0x9e,0xc6,0x9a,0xd8,0x7c,0x9a,0x9c,0xe0,0x63,0x45,0xab,0xd2,0x08,0x77,0x9c,0x72,0xa9,0xb6,0x12,0xa2,0x05,0x07,0x81,0x1f,0xad,0x62,0x67,0x97,0x44,0x1a,0x95,0x94,0x4e,0x05,0xd0,0xfe,0x76,0x3f,0x21,0x10,0x36,0x2c,0x9e,0x8a,0xf8,0x57,0xe4,0x29,0x4c,0x63,0x62,0x91,0x49,0xcc,0x19,0x95, + 0x95,0xf1,0xe5,0xbd,0x6f,0x64,0x31,0x2c,0x12,0xd0,0xcc,0x91,0x0c,0xa5,0x69,0x86,0x99,0x59,0x06,0xa2,0x0b,0x7c,0x34,0x00,0xae,0x8b,0x3c,0x65,0x1b,0xfc,0x1e,0x3e,0x78,0xe0,0x0d,0xf2,0xb1,0x44,0x7e,0x12,0x5e,0x48,0x8e,0x07,0x16,0xf7,0x9e,0x80,0x3e,0xd1,0xf8,0xe5,0xe2,0x82,0x49,0x15,0x57,0x4e,0xca,0x03,0xc5,0x66,0xff,0x73, + 0x93,0x1c,0xf9,0xc9,0xa9,0xad,0x70,0x94,0x20,0x0c,0xf6,0x0d,0x83,0x83,0x14,0x89,0xb4,0x37,0xbd,0x3f,0xed,0xb5,0xf0,0xf9,0xbb,0x1b,0x9d,0x9e,0x33,0xb6,0x29,0xd0,0x91,0x21,0x76,0x2a,0x8b,0x05,0x11,0x5c,0x13,0xc4,0xec,0xc8,0x37,0xc6,0xd2,0xf3,0x21,0x5b,0xea,0xaa,0x2d,0x58,0x6e,0xfc,0x90,0x24,0xbb,0x3b,0xc0,0x7a,0xb7,0xd5, + 0x4a,0xdf,0x91,0xb3,0xb4,0x31,0xb5,0xa6,0xc4,0x32,0x31,0xfb,0x71,0x81,0x9c,0x56,0x3d,0xa6,0xf8,0x85,0xa7,0x78,0x1b,0xe5,0x66,0x6e,0x9d,0x32,0x7a,0xc8,0xf7,0x19,0x4a,0xe1,0x24,0xbe,0x64,0xe9,0x54,0x2e,0x6e,0x10,0x6a,0xd5,0x7d,0xce,0x8a,0x44,0x0f,0x60,0x90,0xe6,0x56,0x9e,0x1d,0xc1,0xe4,0xd1,0x5f,0xb4,0x3f,0x0f,0xd3,0xb2, + 0xb3,0x52,0x50,0x90,0xd3,0xd9,0xde,0xff,0x99,0x84,0x60,0x61,0xc2,0xda,0x0c,0x1a,0x65,0x53,0xc3,0x83,0x59,0x39,0x74,0x33,0x78,0x55,0x25,0xb3,0x79,0x89,0x68,0x8c,0x56,0xda,0xf2,0x5c,0x5a,0x98,0xea,0x82,0x11,0xfa,0xd6,0x32,0x02,0x2d,0x91,0xc7,0x5e,0x85,0x3c,0x4b,0xb2,0xcf,0x0b,0x73,0x9d,0xa0,0x1b,0x1c,0x0e,0x6b,0xf5,0x53, + 0xf9,0x0b,0x46,0xe0,0x23,0x16,0xe0,0x03,0xe8,0xb2,0x3f,0x9e,0x1b,0x01,0xf3,0xd6,0x8b,0x2f,0x06,0x62,0x17,0x7c,0x60,0xb6,0x96,0x61,0x80,0x65,0xad,0x51,0xbe,0x40,0x72,0x5a,0xd7,0xc0,0x73,0x36,0xc3,0x8e,0xe3,0x29,0x33,0x15,0x14,0x7e,0xc1,0x9d,0x64,0x9d,0x07,0x70,0x39,0xa3,0x06,0x7c,0xae,0x7b,0x95,0x7a,0xcf,0xda,0x21,0x4c, + 0x0e,0x68,0xb7,0x03,0x7c,0x85,0xb7,0xfc,0xc9,0x75,0x26,0xec,0x77,0xa8,0x00,0x14,0x43,0xfc,0x28,0x42,0xe8,0x7b,0x01,0xec,0xf9,0x69,0x1a,0xbb,0x8b,0xc3,0x5f,0x32,0x6a,0xe1,0xc0,0x49,0x0c,0x41,0x83,0xba,0xe9,0x2f,0x69,0xdf,0x4f,0xda,0xa0,0xb7,0x87,0x98,0x16,0x90,0x42,0x2c,0x45,0xd8,0xde,0x54,0x97,0xdb,0x77,0xbf,0xf9,0x18, + 0xea,0x36,0xf3,0x4f,0x7f,0x86,0x2c,0x99,0x82,0x6e,0x0d,0x99,0xf1,0xe0,0xce,0xf8,0x1c,0x19,0x9a,0x1c,0x49,0x1a,0xe0,0x91,0x3a,0x75,0x3e,0xff,0x8e,0xc5,0x7b,0x46,0x63,0xb8,0xef,0x2d,0xde,0x48,0x32,0x63,0x28,0x44,0xae,0x4a,0xf1,0x35,0xfd,0x8f,0xa8,0x38,0x9d,0xd0,0x0a,0x9d,0x3f,0x74,0xde,0xe6,0x3d,0xb0,0xd3,0x4b,0xc1,0x8b, + 0xb5,0x84,0x72,0x28,0xc2,0xd2,0xd6,0x81,0x82,0xea,0xfc,0x98,0xac,0xc3,0x68,0x81,0x85,0xbf,0x48,0xa9,0x4b,0x01,0xe9,0x0f,0x46,0xd5,0x65,0x91,0xb5,0xfe,0x95,0xf1,0xc3,0x7e,0xe2,0xa1,0xa4,0x25,0xe3,0x93,0x1b,0xf5,0x85,0x1e,0x99,0x48,0x36,0x50,0xe8,0x34,0xf3,0xf4,0x15,0xa6,0x06,0xc4,0xca,0x66,0xb5,0x8f,0xc8,0xbf,0xa9,0x97, + 0xa6,0x3c,0x66,0x9c,0x2d,0xc9,0x12,0x94,0x4b,0x0f,0xb8,0x37,0x84,0x66,0xd8,0xcc,0x47,0xd7,0xfc,0x5f,0x79,0xf5,0x92,0xb3,0x88,0xcc,0xf7,0xd6,0x2c,0xaf,0x9b,0x62,0x55,0x71,0xdb,0x3c,0xbf,0x8d,0x73,0x73,0x0e,0x71,0xf7,0xfc,0x11,0x9f,0x4b,0xca,0x12,0x68,0x12,0x14,0xc3,0x07,0x68,0xe5,0x98,0xc4,0x85,0x45,0x97,0xa7,0x66,0x41, + 0x2b,0x58,0xa4,0xcf,0x06,0xe4,0x67,0x4a,0x4f,0x18,0xb0,0xb7,0x73,0xfc,0xfe,0xd9,0xb5,0xe8,0x73,0xe0,0x89,0xd1,0x9c,0xd9,0xe7,0x24,0xe8,0xe7,0xf9,0xd7,0xf5,0x68,0x84,0xda,0x48,0xdf,0xc8,0x81,0xd2,0xf8,0xdf,0x0d,0x3e,0x3f,0x68,0x69,0xf0,0xdc,0xac,0x41,0xee,0xc4,0x47,0xff,0x61,0x9b,0x60,0xe1,0xae,0xd5,0x4f,0x70,0xd5,0xcd, + 0xb9,0x28,0x65,0xb7,0xc9,0xca,0xf8,0x52,0xcc,0xdb,0x24,0xa0,0xe9,0x67,0x4c,0xce,0x7b,0x0e,0x5c,0x1f,0xea,0xfe,0x62,0x66,0x70,0x40,0xab,0xbd,0x87,0x05,0x8e,0xb5,0x45,0x50,0x02,0x45,0xaa,0xc7,0x65,0x95,0x39,0xe4,0x6f,0x01,0x56,0x9c,0x81,0xdb,0x84,0xb8,0x82,0xf6,0xbe,0xe5,0xa7,0xb8,0xa4,0xe0,0x4c,0x63,0x65,0x91,0xee,0x60, + 0xec,0xa7,0x18,0x2b,0xba,0xd8,0x20,0x6d,0xbb,0xe5,0x15,0xc3,0x08,0x7f,0x81,0x2d,0x0b,0xaf,0xe2,0xa5,0xdf,0x13,0x85,0x3f,0xc0,0x4e,0x07,0x10,0x61,0xcb,0xf8,0x49,0xfb,0x09,0x2b,0x86,0x43,0xe1,0xe8,0x49,0x46,0x9a,0x7e,0x22,0xdb,0xfd,0x37,0x9f,0x09,0x80,0x5b,0xcd,0xa0,0xb3,0xe6,0x5b,0xab,0xa8,0x3a,0x87,0xa9,0x30,0xc9,0x9a, + 0xc5,0x63,0x5c,0xc3,0xe4,0xb4,0xdc,0xc2,0x32,0x18,0x78,0x81,0xad,0xe7,0xa6,0x8f,0xe8,0xa7,0xf8,0x2b,0x53,0x71,0xe7,0x44,0x94,0xb2,0xb1,0x8e,0xcf,0x8b,0x50,0x88,0x5e,0xa6,0xdd,0xef,0x7f,0x46,0x2e,0x5e,0x09,0x40,0x06,0x1d,0xe6,0x3f,0xae,0x88,0x6f,0x2f,0xa5,0x52,0xae,0xfb,0x7d,0xeb,0xca,0xd6,0xc4,0x6c,0x81,0x61,0x52,0x9c, + 0xda,0xe6,0x23,0xf9,0x2c,0xe4,0x99,0xbe,0xc7,0x43,0x0a,0xff,0xfd,0xff,0x06,0xb6,0x1b,0x93,0x21,0x9b,0xda,0xf7,0xaf,0xd5,0x09,0x74,0xc7,0x20,0x71,0x7d,0xe4,0x4d,0x91,0x95,0x7c,0xaa,0x5f,0x9d,0xd6,0x8a,0xe7,0x57,0xd4,0x64,0xef,0x33,0x94,0xb9,0x62,0xbf,0x7a,0x8e,0x77,0x2e,0x6e,0xb5,0x2b,0x36,0xa6,0xdd,0x0e,0xa7,0x37,0xab, + 0xdf,0x11,0x23,0x99,0x97,0x3f,0x01,0x2d,0xe9,0x68,0x41,0x26,0xa9,0x40,0x58,0x27,0x2b,0x44,0x99,0x66,0x1b,0x7b,0x37,0x68,0x9e,0x54,0x57,0xa1,0xa2,0x83,0xdc,0x10,0x74,0xda,0xcf,0x79,0x89,0x30,0xb1,0x22,0xbb,0x8e,0x4b,0x39,0xe4,0x21,0xc9,0x2b,0xae,0xe3,0x0b,0xd1,0x12,0x68,0x9e,0x58,0x0e,0x72,0x7a,0xaf,0x7d,0x36,0xbf,0x83, + 0x12,0x00,0xc0,0x12,0x83,0x55,0xa5,0xb4,0x7c,0xc4,0x18,0xb8,0x64,0x9b,0x4f,0x40,0xc5,0xa5,0xb2,0x2f,0x30,0x90,0x5c,0x32,0x81,0x25,0x74,0xda,0xdc,0x0b,0x36,0xe0,0x34,0x04,0x53,0x4e,0x3a,0x76,0x11,0xed,0x30,0x9d,0x3f,0x1b,0xb8,0xd1,0xf0,0xa8,0xb2,0xed,0x76,0x94,0x07,0x08,0x56,0x7e,0x03,0xcf,0x34,0x69,0x1b,0xe9,0x5c,0xfe, + 0xd2,0x62,0xa5,0xa2,0x75,0x9b,0x69,0xea,0x89,0x51,0xd4,0x67,0xb2,0xa2,0x30,0xe8,0x91,0x0c,0xd9,0xfd,0x9b,0x28,0xed,0xb3,0x4f,0xd6,0xcd,0x1c,0x37,0x90,0x87,0x4c,0x99,0xf1,0xd1,0xb0,0xd5,0xe1,0xc5,0x86,0x03,0x03,0xc5,0x36,0x3c,0xb8,0xe2,0x5e,0xa5,0x6b,0x93,0xad,0x58,0xd9,0x17,0x01,0x4d,0x74,0x31,0x96,0xb2,0x71,0x15,0x14, + 0x77,0xd8,0x7d,0xd4,0xe3,0x82,0xad,0xd1,0x37,0x25,0x7d,0x16,0x4e,0xc4,0x5c,0x0a,0xf3,0x48,0xf7,0x45,0xdf,0x2f,0x9d,0xd3,0x36,0x11,0xc4,0xfa,0x0d,0x28,0x07,0xb7,0xaa,0x70,0x69,0xce,0x02,0x9a,0xc9,0x4f,0x05,0x04,0x73,0x56,0x20,0xc5,0x75,0xea,0x73,0x97,0xa7,0x57,0xef,0x5c,0x5a,0xe9,0x4c,0x1c,0xaa,0x51,0xf6,0x92,0xdb,0x5c, + 0x28,0xb6,0x76,0x38,0x90,0x56,0xc8,0x0a,0x7d,0x4d,0x35,0xdb,0x27,0xe1,0xf5,0x94,0x51,0xe0,0x77,0x60,0xad,0xd0,0x6e,0xc7,0xc7,0xde,0x03,0x21,0x80,0x58,0x62,0xf2,0x70,0x7e,0x6f,0x29,0xbe,0xf0,0x1f,0xeb,0x81,0x0e,0x9a,0x8c,0xaf,0x1c,0x69,0x4a,0xc7,0x4d,0x82,0xa0,0x35,0xb6,0xe7,0x42,0x68,0x9a,0xba,0x7d,0xbb,0xc1,0xc9,0x77, + 0x7b,0x86,0xa4,0xf4,0xfb,0x83,0x2c,0x17,0xee,0xb8,0xa2,0x89,0x73,0xaf,0x5b,0x8c,0xed,0x63,0xc1,0x22,0x95,0x98,0xe3,0xa5,0x64,0xe6,0xb9,0xd1,0x51,0x36,0xe8,0xf3,0x2e,0xc4,0xd7,0x70,0x2f,0xe1,0xa6,0xb2,0x72,0xb1,0x8e,0x3e,0xe3,0xf7,0x24,0x85,0x26,0xe6,0x5f,0xfc,0xef,0x09,0x0c,0xf8,0xe2,0x0b,0xd6,0x18,0xc6,0x03,0x04,0xcc, + 0x42,0x7e,0x47,0xea,0x45,0xa5,0xcc,0x0c,0x1f,0xba,0xe3,0x15,0xba,0x93,0x38,0xef,0x73,0x30,0x84,0xbb,0x77,0x9e,0x8c,0x1d,0xef,0x96,0x09,0xcd,0x17,0x57,0xa5,0xf9,0x19,0x5c,0xf9,0x08,0x33,0xb9,0xe1,0x72,0xa7,0xc4,0x02,0x15,0xca,0x0e,0xeb,0x27,0x5b,0x3e,0xc4,0xd9,0x89,0xe8,0xd7,0xc4,0x7c,0x98,0xaa,0x2b,0xf0,0x14,0xd8,0x52, + 0x3c,0x90,0xed,0x4a,0xff,0x0a,0xa2,0x27,0x30,0xc6,0x12,0xf9,0x6c,0xfd,0xda,0x97,0xf9,0x07,0x73,0x23,0xe4,0x04,0xaf,0xab,0x44,0xb0,0x35,0x63,0x40,0xc8,0x34,0x3e,0x03,0xd0,0x25,0x40,0xab,0x1d,0x9a,0xbf,0xad,0x85,0x25,0x3e,0xf1,0xa5,0x0d,0xf4,0x8a,0xba,0x70,0xb0,0x2e,0x09,0x73,0x21,0x71,0x42,0x73,0xfa,0x6f,0x33,0x05,0x7f, + 0xd5,0xdb,0x76,0x0e,0xc7,0xf7,0x8f,0xad,0xfb,0xb5,0x2b,0x44,0xb2,0xae,0xda,0x29,0x05,0x91,0xf5,0xeb,0x32,0x9a,0xf3,0xf2,0x8e,0x14,0x61,0xf6,0x7b,0x4a,0xd6,0xf6,0x97,0xa0,0x5f,0x4c,0x19,0x56,0xc1,0xe7,0xba,0xc6,0x11,0x18,0x07,0x48,0xa4,0x34,0x52,0x6d,0x45,0xf4,0x0b,0x91,0xce,0xfb,0x81,0xce,0x97,0x9b,0xbd,0xa1,0x0a,0x36, + 0x9b,0xd2,0x02,0xe6,0x03,0x71,0x28,0xc1,0x36,0x0c,0xd2,0x89,0x71,0xbd,0x7b,0xd7,0x6a,0x5b,0x3c,0x01,0x47,0xb9,0x30,0x70,0x38,0xd8,0x2d,0x30,0xb8,0xa7,0xb8,0xc0,0xf5,0x5e,0x22,0x56,0xe8,0xd0,0x04,0x92,0xa7,0xce,0x4c,0x7f,0x34,0xfc,0xc2,0x7e,0xc4,0x56,0x63,0xea,0x3d,0x95,0xa6,0x9f,0xe7,0x12,0xda,0xfd,0xb8,0xa6,0x26,0x77, + 0xff,0x61,0x67,0x32,0x7b,0x3d,0x43,0xe4,0x27,0xd2,0xbf,0x40,0xfd,0x57,0xc6,0xce,0x45,0x55,0xba,0x94,0xc9,0x03,0xc5,0x31,0x40,0x3f,0xe5,0x7e,0x91,0x32,0x39,0xb9,0x6c,0xa5,0x7e,0x19,0xee,0x9c,0xb1,0xad,0xf5,0xa5,0xe9,0xea,0x02,0x37,0xfe,0x93,0xdd,0xe2,0xae,0x43,0xdb,0x3d,0xc0,0x6e,0xbf,0x1b,0xa2,0x44,0x1c,0x99,0xee,0xd6, + 0x9c,0x0a,0x19,0xe9,0xbd,0x4a,0x9b,0x74,0xca,0x91,0x19,0xff,0xf5,0x56,0xbf,0xa1,0x29,0x6c,0x87,0xfe,0xa7,0x4a,0x14,0xec,0x61,0xc7,0x9d,0x10,0xdc,0x39,0x03,0xbc,0x93,0x20,0xa3,0x6f,0x62,0x3e,0x2a,0xdc,0x11,0x93,0x1e,0x51,0xb7,0xa5,0x66,0xe2,0x85,0x05,0xc1,0xe9,0x9f,0x82,0x76,0xef,0xe8,0xcb,0xf2,0x8c,0x3c,0xa1,0x30,0x60, + 0x4c,0xf0,0xb1,0xbd,0x30,0x74,0x62,0x3d,0xc4,0x64,0xb7,0x89,0x91,0x15,0x34,0x46,0x7b,0xfa,0xa2,0x89,0xab,0x6b,0xc2,0x8d,0xba,0x2e,0x5a,0x73,0x81,0x30,0x49,0xb2,0x04,0xa8,0x1c,0x3f,0xbe,0x4c,0x79,0x8e,0x59,0x35,0x7d,0xf7,0x53,0xea,0x4a,0x56,0xbb,0x64,0x84,0xac,0x6f,0x0c,0x93,0x87,0xb5,0x5a,0xcc,0xda,0xfd,0x90,0xf8,0x0c, + 0xe3,0x56,0xdf,0xf2,0xed,0x44,0x15,0xf5,0xcf,0x96,0xc6,0x71,0x2c,0x02,0xa1,0xe7,0xc0,0xe0,0xc9,0x00,0x0f,0x78,0xe9,0xba,0x2b,0x9e,0x60,0xad,0x78,0x3a,0xe5,0x12,0xfe,0xeb,0x3d,0xc7,0xc3,0x4c,0x95,0xc5,0xc2,0xb3,0x0e,0x1c,0x84,0x92,0xab,0xf7,0x5d,0x04,0x4c,0x94,0x55,0x54,0x08,0xd3,0x3f,0x8e,0x49,0xd5,0xb9,0x2d,0x51,0xab, + 0x47,0x7f,0x95,0xe8,0x2c,0x21,0x8e,0x43,0xa7,0xe9,0x5c,0x92,0x65,0x32,0x87,0x48,0xce,0x56,0xdc,0xdf,0x24,0x27,0xdb,0x81,0x9d,0x3e,0xd2,0xae,0x6e,0x13,0x20,0x57,0xdc,0xe3,0xfe,0x78,0x7d,0xf0,0x18,0x8d,0x2f,0x02,0x0a,0xc5,0x67,0xc2,0x89,0x2d,0x84,0x21,0xf3,0x0e,0x69,0x80,0xcf,0xb0,0xe8,0xab,0xc5,0xab,0x19,0xec,0x82,0xe6, + 0x7c,0xec,0x99,0x4c,0xfd,0x43,0xfb,0x02,0x8d,0x4c,0x93,0x89,0x57,0x2a,0xba,0xfa,0x3f,0xde,0xac,0xd7,0x64,0x57,0x2c,0x0c,0xb2,0x73,0xc0,0x56,0xb5,0x46,0x81,0xfc,0xa6,0x23,0xa6,0xcd,0xf6,0xb3,0xb0,0x68,0xb7,0x4d,0xc5,0xdb,0x25,0x29,0x34,0x7a,0xd8,0x3a,0x24,0xf1,0x67,0x6e,0x57,0x2c,0x07,0xf5,0x13,0x28,0x99,0x2e,0x84,0x42, + 0x9f,0xef,0xbe,0xb5,0xf9,0x46,0xc6,0x4c,0x55,0xf1,0x76,0xbf,0xdf,0xd3,0xb0,0x3b,0x78,0xa3,0x6a,0xc8,0xc0,0x62,0x9d,0x4e,0x2c,0xd8,0x22,0x7b,0xff,0x95,0x1b,0xf5,0xfa,0x55,0x5f,0xce,0x5c,0x5e,0xec,0x5d,0xfa,0xc1,0xfc,0x45,0xf6,0x67,0xa0,0x30,0x14,0x4b,0x48,0xd4,0xb5,0xf6,0xb7,0xac,0x77,0x2d,0x17,0xa6,0xfc,0xe9,0x49,0xce, + 0x58,0x0f,0x88,0xb3,0x16,0x33,0x39,0x26,0x04,0xb2,0x61,0x9c,0x5e,0x83,0xa5,0x27,0x61,0xc9,0x1c,0x7d,0xa9,0xeb,0xba,0x8b,0x25,0xab,0xd5,0x6e,0x3c,0xfb,0x6e,0x2d,0xb4,0x14,0xa0,0x5e,0xf6,0x47,0x7a,0xfe,0x54,0x05,0xfc,0x8d,0x9a,0x1c,0x80,0x7d,0x91,0x95,0x86,0x80,0x94,0xee,0xd1,0x0c,0x34,0xca,0xf4,0x4a,0xfb,0x2c,0x96,0xed, + 0xba,0x71,0x80,0x0b,0x92,0x68,0x4f,0xa0,0x39,0x1c,0x41,0xe5,0xb9,0x3a,0x71,0x76,0x97,0xed,0x36,0x4f,0x9f,0xe3,0xa5,0xba,0x1d,0x0e,0x12,0x75,0xb0,0x92,0x40,0x92,0xca,0xf4,0x72,0xce,0x10,0x5b,0x1c,0x3c,0x4f,0x4b,0x02,0xaf,0x61,0xdc,0x54,0x1c,0x70,0x85,0xc5,0xee,0xec,0x80,0x0c,0x1a,0xe2,0xd0,0x45,0x8c,0xb1,0x6b,0x6d,0x8b, + 0xad,0x4b,0x22,0x0c,0xae,0x2e,0x9b,0x56,0x7a,0xad,0x80,0x13,0x28,0x60,0x3f,0x16,0x16,0xb4,0x18,0x73,0xe3,0x1e,0xdf,0x9a,0xbb,0x09,0xa0,0xfa,0x9e,0xdb,0x9c,0x14,0x63,0x29,0x7c,0x8e,0x26,0x23,0xcc,0xf0,0x9b,0x54,0x0d,0x14,0xd6,0xa7,0x48,0x36,0xbc,0xb4,0xe6,0x5b,0x05,0xc8,0x74,0x22,0xb8,0xd9,0x0a,0xd5,0xb8,0x3e,0x20,0xbe, + 0x12,0x34,0x50,0xeb,0xa8,0x55,0x38,0x1b,0x16,0x55,0xa8,0x17,0xb8,0xec,0xb4,0x46,0x8c,0x72,0x21,0xe5,0x90,0xdf,0x68,0x95,0xd2,0x58,0xc9,0x73,0x8f,0xb9,0x45,0x1d,0x53,0xcd,0xbf,0xd1,0x01,0xba,0x59,0x74,0x38,0xae,0x4b,0x6a,0xd9,0x7a,0x5c,0xaf,0x0e,0x37,0xcb,0x0d,0xe7,0xce,0x37,0x06,0x07,0xba,0xc8,0xc0,0xef,0xbe,0x88,0x39, + 0x69,0xa2,0xe1,0x77,0x8b,0x2a,0xbc,0x81,0x29,0x26,0xb1,0xaa,0x90,0x9c,0x0c,0x4d,0x4e,0xbc,0x66,0x7c,0x0b,0xad,0xbf,0x4e,0x8f,0x8b,0x26,0xfd,0x7f,0x1c,0x30,0xa4,0xf2,0x19,0xdb,0x06,0x2d,0xfd,0x47,0x05,0x7e,0xb4,0x96,0x92,0x03,0x1d,0xbe,0x94,0x4e,0x6f,0x68,0x35,0x14,0x8c,0x12,0xe6,0x83,0x1c,0x00,0x15,0xf7,0x8d,0xc0,0x93, + 0x0e,0x0c,0xf1,0xc7,0xde,0xa2,0xa3,0x7d,0x90,0x78,0xc0,0x37,0x81,0x05,0x93,0x6c,0xeb,0x79,0x06,0x3b,0xd4,0xc3,0x06,0xee,0xab,0x7a,0x1c,0xd1,0x5f,0x8e,0x63,0x0e,0x32,0xf1,0xa8,0xef,0x6a,0xe1,0x34,0x63,0x28,0xea,0x47,0xaf,0x23,0xc1,0xc2,0x53,0x5f,0x36,0x0a,0x97,0x68,0x94,0xfb,0x36,0xea,0x05,0xaa,0xac,0x8f,0x7a,0x07,0x81, + 0x71,0x18,0xa0,0x3f,0x1d,0xd9,0xf2,0x85,0x58,0x62,0xc5,0xb7,0x95,0x35,0x55,0x7b,0x95,0x90,0x3e,0xe4,0x4c,0x8a,0xe0,0x6f,0x41,0x2e,0x00,0x8e,0x05,0xd4,0xdb,0x79,0x90,0x5c,0x29,0x93,0x35,0x11,0x25,0x67,0xb2,0x86,0x12,0x5e,0x87,0xe6,0x43,0x59,0x80,0x5b,0xef,0x7a,0x44,0x55,0xc7,0x40,0x42,0xa8,0x13,0x16,0xe1,0xe5,0x09,0x26, + 0xb5,0x51,0xa5,0x89,0x11,0x50,0x39,0x4e,0x72,0x69,0x7f,0xd1,0x38,0x0c,0x11,0x5c,0xe8,0xa0,0x57,0xa7,0xdd,0x8d,0x38,0xe1,0x9f,0xf6,0xcb,0xa4,0xe8,0x85,0x82,0x9d,0x5f,0xbb,0x70,0x85,0x11,0x0e,0xe0,0x59,0xeb,0xf4,0x1c,0xa5,0xd7,0xf7,0x7e,0x93,0x06,0xd5,0xb8,0x9f,0xa7,0x4a,0xaa,0xa0,0x86,0x01,0x68,0x79,0x13,0x05,0x1f,0x4f, + 0x4d,0x8c,0x9a,0x7d,0x13,0xf4,0x4c,0x49,0x73,0x56,0xa9,0xb5,0xcb,0xa5,0x74,0x6a,0x7b,0x7e,0x09,0x04,0x9a,0xd5,0xac,0x3e,0x4c,0x3b,0xa3,0xeb,0x9a,0xed,0x81,0x73,0x11,0xd7,0xbb,0x60,0xb4,0x37,0x72,0x81,0x2e,0x73,0x3f,0x46,0xa7,0xa3,0xf9,0xa7,0xb3,0xdd,0x10,0x15,0x20,0x1b,0x27,0x22,0x7a,0x96,0x46,0x81,0x0f,0xe9,0x0b,0x09, + 0xbb,0x4f,0x4d,0x4d,0xa9,0x29,0x5d,0x39,0x62,0x9c,0xeb,0x42,0xab,0x84,0x57,0x27,0xc6,0x75,0xe7,0xda,0x50,0xe8,0x11,0x48,0x19,0x13,0x2a,0x6e,0x6f,0xf3,0xe6,0x65,0x08,0x49,0xc8,0xfc,0x69,0xf3,0xb6,0xb2,0x40,0x85,0x7b,0x2d,0x82,0x82,0x01,0x21,0xa8,0xaa,0x46,0xfc,0x06,0xd0,0xd7,0xa2,0x4d,0x25,0xf2,0x35,0x79,0xab,0xb4,0x55, + 0xb0,0xc2,0x8e,0x0d,0x10,0x16,0x8e,0x70,0x2f,0x28,0x19,0xd5,0x55,0x14,0x9f,0xd4,0x73,0x9a,0xb9,0x65,0x70,0x60,0x39,0x02,0x6f,0xe8,0xf1,0xc5,0x3d,0x99,0x2d,0x12,0x44,0x52,0x84,0xb5,0xdd,0xb7,0xf0,0xd2,0xec,0x87,0x5f,0x06,0x38,0x5b,0xb0,0x08,0x9e,0x5e,0x4e,0x78,0x9f,0x0f,0xcc,0x13,0x00,0xeb,0xef,0x36,0x65,0x4c,0x6e,0x42, + 0x4e,0x1e,0x87,0xfe,0xf4,0x07,0x96,0x1a,0xfb,0xc4,0x4c,0x1c,0x16,0xc6,0xb2,0xb8,0x12,0x11,0x10,0xa9,0xf0,0x82,0xc1,0xb8,0xb5,0xd7,0x87,0x92,0x53,0x82,0xf5,0xf6,0x7a,0x3d,0x7c,0xa8,0xd4,0x6a,0x06,0x36,0xe6,0xbf,0x88,0xfa,0x7e,0x83,0x3e,0x15,0x88,0xb9,0x84,0x71,0xee,0xab,0xa4,0xc6,0x38,0x74,0xaf,0x2f,0x1b,0xe1,0xcd,0x0d, + 0x8d,0x7a,0xcb,0xe6,0x1c,0xfe,0x29,0x24,0xa5,0xdd,0xfd,0x71,0xc0,0x26,0xdc,0x7b,0xd5,0xe6,0xd4,0x98,0x9d,0xd3,0xe8,0xd2,0xd6,0xd4,0x51,0x4e,0xfa,0x7f,0xc7,0x18,0xbd,0x47,0x77,0x88,0x28,0x0a,0xc4,0x73,0xf2,0x5e,0xff,0xef,0xca,0xb0,0x65,0xc1,0x47,0x18,0x80,0x26,0x5e,0xa1,0x4e,0x8b,0x94,0xf3,0xea,0x50,0x46,0xa3,0xad,0x8c, + 0x80,0xf6,0xbd,0xa7,0x31,0x20,0x5f,0xd9,0xf0,0x01,0xcc,0x7a,0x38,0xa7,0xd7,0xa0,0x4b,0x56,0x4b,0xe3,0x0a,0x81,0xa9,0xc3,0xc2,0x98,0x1b,0xb8,0xe4,0xb5,0xa4,0xdf,0x06,0xd3,0xd7,0x83,0xc2,0x35,0x03,0x59,0xdc,0xaf,0x2f,0x4b,0x4d,0x0f,0x36,0xa1,0xcd,0xba,0xfb,0x00,0x04,0xf3,0x55,0x16,0x7e,0x79,0x8b,0x54,0x74,0x21,0x74,0x72, + 0xd1,0x2b,0xc0,0x8d,0x74,0x68,0xe9,0xd7,0xe0,0xa6,0xdd,0x11,0x06,0x71,0x2f,0xca,0xb0,0xca,0xe7,0x9f,0x5a,0x1d,0x05,0xc1,0xfb,0xba,0xeb,0x3c,0x0b,0x01,0x40,0xc5,0x6b,0x69,0xb7,0xd0,0x20,0x34,0x1c,0x8b,0x49,0x71,0x83,0x61,0x7e,0xfc,0xfd,0x40,0x30,0x5d,0xda,0x2f,0x86,0x0e,0x0e,0xbe,0x40,0x12,0x73,0x4f,0x56,0xea,0x04,0x92, + 0x87,0x73,0x5b,0xa3,0x0f,0xb9,0xe3,0x38,0x0e,0x04,0xce,0x0b,0xd8,0x0f,0xf2,0xda,0x8f,0x36,0xf2,0x91,0xf2,0x03,0xd1,0xea,0x3e,0xe2,0x76,0xa2,0xa6,0xcd,0xb4,0xb4,0xaf,0x6b,0x02,0xee,0x9e,0x53,0xb3,0x78,0xc0,0xf4,0xe8,0x60,0xbc,0x84,0x4a,0x42,0xc0,0xd2,0x13,0x05,0x00,0xda,0x2b,0x1f,0xaa,0x9e,0x69,0x13,0x3c,0xa1,0x75,0x5b, + 0x44,0x1e,0xb7,0xc6,0xac,0x09,0xe6,0xb8,0xfa,0x29,0xb1,0xe3,0x40,0x44,0x4f,0xa5,0x85,0x37,0x3e,0xf3,0x0f,0xa2,0x2d,0x88,0x94,0x9f,0x95,0x9b,0xe9,0x17,0x9d,0xc2,0x26,0xf4,0xcf,0x51,0xad,0x6b,0x45,0xac,0x06,0x32,0x11,0x54,0x67,0x01,0xe3,0x99,0x64,0x37,0xcc,0x04,0xda,0xac,0xcb,0x70,0xa4,0x11,0xbd,0xe6,0x86,0xb2,0x87,0xfe, + 0x71,0xe1,0x2c,0x87,0xd3,0x7d,0x68,0x9c,0x41,0x8c,0xbd,0x34,0xaf,0xac,0x99,0x22,0x59,0x6e,0x92,0x8b,0x37,0xcd,0x39,0x17,0xd4,0x98,0x9e,0x9e,0xf1,0x04,0xa5,0x8e,0xb7,0xd8,0xf4,0x16,0x46,0x81,0x02,0x35,0xad,0x75,0xd8,0xc1,0xed,0xfe,0xc3,0x1c,0x2e,0x5f,0x42,0x7d,0xdd,0xf5,0x11,0x47,0xf5,0xe4,0x86,0x0c,0xaa,0x3b,0x00,0x83, + 0x2e,0xa3,0x81,0xa5,0x0b,0xc6,0x5d,0x41,0xc2,0x73,0x19,0xc0,0x1e,0x71,0x79,0x37,0xe9,0xc5,0x0d,0xdd,0x67,0x95,0xa3,0x6d,0xbe,0xff,0x1b,0x07,0xf6,0x53,0x42,0x31,0x73,0x06,0xde,0x1f,0x7f,0x3c,0xd1,0xc9,0xd3,0x6b,0xca,0xc9,0x1d,0xbe,0x55,0x90,0x7f,0x78,0xe3,0xec,0x6e,0x82,0x3e,0x4a,0x14,0x7e,0x98,0x7e,0x42,0xe9,0xde,0x9a, + 0x9e,0xc4,0x5c,0x13,0x53,0x02,0x7f,0xa9,0x63,0xed,0x08,0x9b,0xc5,0x94,0xd0,0x49,0x24,0x4e,0x4c,0x84,0xcb,0xfe,0xcb,0xe1,0x3f,0x00,0x37,0x38,0xe3,0x62,0xf2,0x2d,0xc9,0xaf,0x96,0x58,0x52,0x0d,0x5c,0x77,0x4f,0xeb,0xe6,0x07,0xc4,0x70,0x3b,0x60,0x1d,0x88,0x4f,0xf1,0xf8,0x59,0x3f,0xc5,0x25,0xe8,0x7c,0x57,0xea,0xe5,0x80,0xaa, + 0x1c,0x07,0xa0,0xbf,0xc8,0x52,0xdc,0x00,0x9d,0xe4,0xcf,0xbc,0xd2,0x49,0x57,0x84,0x75,0xb6,0x11,0xa8,0xeb,0x41,0x0d,0xa8,0xbe,0x36,0x6d,0x18,0xdc,0x6d,0x6a,0x00,0x8d,0x82,0x58,0xda,0x4a,0xcf,0x4d,0x66,0x19,0x26,0x37,0x3d,0x37,0x7f,0xa2,0xe1,0xe4,0xbf,0x69,0xfa,0xde,0xf5,0x43,0x90,0x98,0x5f,0x04,0xec,0xd7,0x96,0x24,0x58, + 0x9a,0xdb,0x38,0x3c,0x73,0x38,0xde,0x67,0x8a,0x9f,0x8c,0x8f,0xd5,0x1f,0x28,0x0e,0xa6,0x28,0xf8,0x97,0x27,0x6b,0x4e,0xfb,0x63,0x6d,0x3c,0xc6,0x4c,0xcd,0x5e,0xb2,0x9b,0xb9,0x54,0x05,0x67,0x06,0x4e,0x39,0x4d,0x8b,0x42,0x77,0xa6,0xbd,0xa0,0x11,0xda,0x1e,0xf1,0x0b,0x1b,0xe4,0x0e,0xb5,0xf1,0x14,0xff,0xb9,0x76,0x77,0xe5,0xdb, + 0xed,0xf9,0x4d,0x4f,0x53,0x65,0xc2,0x7d,0x00,0x18,0x70,0x74,0x5d,0xeb,0x4f,0x79,0x53,0xa0,0x42,0xd4,0x70,0x51,0x28,0xcc,0xa2,0x26,0x67,0x75,0xb1,0x5c,0x29,0x6a,0xe9,0x00,0x48,0x7d,0x13,0x3f,0x15,0xf6,0x0c,0xc6,0x25,0x7c,0xf7,0x71,0xa4,0x5d,0x15,0xc9,0x14,0xb2,0x28,0xb9,0x5d,0x81,0xa8,0x75,0x33,0xef,0x15,0xe2,0xa7,0xd9, + 0x75,0x15,0x89,0x7c,0x27,0xf8,0xc7,0x8d,0x88,0xea,0x51,0xb2,0x91,0x9b,0xba,0x2b,0xb4,0x23,0xb7,0x0b,0x5e,0x6b,0xde,0x8e,0x6a,0x19,0xe9,0x4f,0x1c,0x22,0xa7,0x5d,0xed,0x8e,0xe8,0x9d,0xb1,0x5d,0x05,0x70,0x2d,0x6c,0xe3,0x55,0x70,0x10,0x8c,0x2d,0x75,0x22,0x92,0x6d,0x83,0x02,0x42,0x5b,0xe1,0x6f,0xa0,0x0c,0x16,0xca,0x56,0x1f, + 0x39,0x17,0xc7,0x2c,0x9d,0x60,0xea,0x41,0x3c,0x33,0xeb,0x45,0x4c,0x20,0xe6,0xfc,0xe1,0x3b,0xc1,0x1a,0x02,0x80,0xb0,0x39,0xde,0x45,0x78,0xf1,0xce,0xfa,0x91,0x5a,0x8c,0x16,0x6a,0x6b,0x42,0x0e,0x8f,0xa4,0x21,0x91,0x22,0x0d,0xa2,0xf5,0xc7,0x31,0xee,0xb2,0xfa,0xe9,0x88,0xe9,0xf6,0xc1,0x1a,0xbc,0x49,0x85,0x53,0x44,0xd6,0xdb, + 0x69,0x64,0xac,0xf1,0x17,0xf8,0xcb,0x02,0x22,0x3d,0x2f,0xe5,0xee,0x08,0x4e,0x09,0x8c,0xbd,0xb1,0x47,0x2c,0x1a,0x21,0x23,0xf6,0xc9,0x6b,0x5d,0x0e,0x89,0xbc,0x99,0x98,0xf5,0x95,0x2f,0x57,0x2e,0x84,0xb1,0x57,0x2b,0xe6,0x9e,0x6b,0xad,0x27,0x59,0x8b,0x9a,0xcd,0xcd,0xfd,0x64,0x5a,0xf1,0x8e,0xee,0x01,0x08,0xf2,0x1c,0x87,0xb7, + 0xf7,0xa7,0x9e,0xfd,0x1f,0xa3,0x65,0xce,0x09,0x7d,0x01,0xa6,0xc3,0xdc,0x4f,0xb9,0xdb,0x4c,0x19,0xbd,0xb0,0x99,0x94,0xac,0xd0,0x4d,0x2b,0xec,0x2a,0x8a,0xf5,0x61,0xdf,0x96,0x89,0xf2,0x13,0x1e,0xbc,0xa7,0x21,0x13,0xec,0xef,0x44,0x9b,0x91,0x2f,0x36,0x1f,0xac,0x71,0xce,0x20,0x6c,0xc6,0xd3,0x03,0x5e,0x54,0x61,0x3e,0x21,0x0f, + 0x75,0xdc,0xa9,0x12,0x1a,0xf3,0x4b,0xbf,0x85,0xe0,0x64,0x36,0x3d,0xa8,0x32,0xad,0xdd,0xcf,0x8a,0x5b,0x87,0x00,0xa8,0x1b,0xa4,0x53,0xce,0xa4,0x1c,0x04,0x31,0x65,0x6b,0x4e,0xb8,0x75,0xcd,0x72,0x59,0xa2,0xb1,0xb5,0xf2,0x58,0x88,0x17,0x33,0x9d,0x87,0x48,0x30,0xc7,0x58,0x15,0xfb,0x89,0xd8,0xb1,0xee,0x57,0x97,0x80,0x8f,0x48, + 0x6f,0xd5,0x2d,0xea,0x07,0x4d,0x06,0x71,0x98,0x4f,0xf8,0x13,0x39,0xaa,0xfa,0x00,0xa9,0x9c,0x6d,0x66,0xa9,0x93,0xe0,0x21,0xc0,0x70,0xbe,0x25,0xe3,0x5b,0x84,0xc2,0x55,0x26,0x5f,0xc4,0x79,0x2a,0xde,0x95,0x4c,0x91,0x43,0xe3,0x77,0x9f,0xde,0x6f,0x25,0x58,0x26,0xd9,0x14,0x4c,0xa7,0x50,0x31,0xe5,0x70,0xfb,0xc8,0xac,0xf3,0x75, + 0x2c,0x18,0x81,0x1b,0x45,0xdd,0x31,0x6b,0xee,0xbd,0x96,0x18,0xb9,0x7b,0xde,0xbb,0x9e,0xd9,0x18,0x83,0xa5,0x97,0xc7,0x6c,0xc0,0xb9,0xe0,0x11,0x76,0xe8,0x3c,0x4d,0x03,0xa9,0xa7,0x05,0xc7,0x56,0xa5,0xb9,0x28,0x93,0xd4,0x2a,0xa1,0xeb,0x67,0x4d,0x64,0x0c,0xb8,0x4a,0x83,0x9c,0x6c,0x59,0x6e,0x0f,0xf9,0xfe,0x0d,0x5f,0x32,0x84, + 0x3a,0xb1,0x8a,0x34,0x0b,0x60,0x03,0xa1,0x35,0xa8,0x35,0xfe,0x4b,0xc2,0x71,0x7e,0x6e,0xbd,0x4f,0x4e,0x44,0xf4,0x69,0x07,0x1f,0x10,0xe1,0xf7,0x32,0xd7,0x50,0x70,0x5e,0x9b,0x77,0xd1,0x56,0x55,0xf2,0xc2,0x45,0xc5,0x8a,0x85,0xbc,0x32,0xa3,0x66,0x22,0x86,0x97,0x53,0x06,0x66,0x79,0x35,0xaf,0x8a,0x92,0x60,0xbc,0x6f,0xbe,0x7e, + 0xe0,0xd7,0x42,0x24,0xb6,0xfe,0xd7,0x6e,0xa8,0x3a,0x09,0x52,0x8f,0xb2,0x88,0xab,0x2a,0x88,0x67,0xdd,0x0a,0x9e,0x80,0x10,0x27,0x4b,0x21,0x09,0xa5,0x31,0x23,0x8e,0x34,0x51,0x23,0xc8,0xe0,0xd8,0xfd,0x4d,0x46,0xbf,0xc8,0x88,0xe4,0xd9,0x50,0xac, }; -/* We can only use the hss tests that have a single level here */ static SLH_DSA_ACVP_TEST_DATA slh_dsa_testdata[] = { SLH_DSA_ACVP_ITEM(slh_dsa_sha2_128s_0, "SLH-DSA-SHA2-128s"), }; diff --git a/test/slh_dsa_test.c b/test/slh_dsa_test.c index a7f5bffffe..95a411a18e 100644 --- a/test/slh_dsa_test.c +++ b/test/slh_dsa_test.c @@ -114,10 +114,42 @@ end: return ret; } +static int slh_dsa_sig_verify_test(void) +{ + int ret = 0; + SLH_DSA_ACVP_TEST_DATA *td = &slh_dsa_testdata[0]; + EVP_PKEY_CTX *vctx = NULL; + EVP_PKEY *key = NULL; + EVP_SIGNATURE *sig_alg = NULL; + OSSL_PARAM params[2], *p = params; + int encode = 0; + + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode); + *p = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(key = slh_dsa_pubkey_from_data(td->alg, td->pub, td->pub_len))) + return 0; + if (!TEST_ptr(vctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))) + goto err; + if (!TEST_ptr(sig_alg = EVP_SIGNATURE_fetch(libctx, td->alg, NULL))) + goto err; + if (!TEST_int_eq(EVP_PKEY_verify_init_ex2(vctx, sig_alg, params), 1) + || !TEST_int_eq(EVP_PKEY_verify(vctx, td->sig, td->sig_len, + td->msg, td->msg_len), 1)) + goto err; + ret = 1; +err: + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_free(key); + EVP_PKEY_CTX_free(vctx); + return ret; +} + int setup_tests(void) { ADD_TEST(slh_dsa_bad_pub_len_test); ADD_TEST(slh_dsa_key_validate_test); ADD_TEST(slh_dsa_key_eq_test); + ADD_TEST(slh_dsa_sig_verify_test); return 1; }