From: slontis Date: Fri, 20 Dec 2024 03:18:27 +0000 (+1100) Subject: Add ML-DSA sign/verify X-Git-Tag: openssl-3.5.0-alpha1~606 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3ab7409f3d9a05608e94b5351789fd85994b6511;p=thirdparty%2Fopenssl.git Add ML-DSA sign/verify Reviewed-by: Viktor Dukhovni Reviewed-by: Tim Hudson Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/26127) --- diff --git a/crypto/ml_dsa/build.info b/crypto/ml_dsa/build.info index f972fed51c6..757d3b8d3e4 100644 --- a/crypto/ml_dsa/build.info +++ b/crypto/ml_dsa/build.info @@ -1,7 +1,8 @@ LIBS=../../libcrypto $COMMON=ml_dsa_ctx.c ml_dsa_encoders.c ml_dsa_key_compress.c ml_dsa_key.c \ - ml_dsa_matrix.c ml_dsa_ntt.c ml_dsa_params.c ml_dsa_sample.c + ml_dsa_matrix.c ml_dsa_ntt.c ml_dsa_params.c ml_dsa_sample.c \ + ml_dsa_sign.c IF[{- !$disabled{'ml_dsa'} -}] SOURCE[../../libcrypto]=$COMMON diff --git a/crypto/ml_dsa/ml_dsa_encoders.c b/crypto/ml_dsa/ml_dsa_encoders.c index 844a1e97928..c72091bfbbe 100644 --- a/crypto/ml_dsa/ml_dsa_encoders.c +++ b/crypto/ml_dsa/ml_dsa_encoders.c @@ -7,29 +7,104 @@ * https://www.openssl.org/source/license.html */ +#include #include "ml_dsa_local.h" #include "ml_dsa_key.h" #include "ml_dsa_params.h" +#include "ml_dsa_sign.h" #include "internal/packet.h" -typedef int (PRIV_ENCODE_FN)(WPACKET *pkt, const POLY *s); -typedef int (PRIV_DECODE_FN)(PACKET *pkt, POLY *s); +typedef int (ENCODE_FN)(const POLY *s, WPACKET *pkt); +typedef int (DECODE_FN)(POLY *s, PACKET *pkt); -static PRIV_ENCODE_FN poly_encode_signed_2; -static PRIV_ENCODE_FN poly_encode_signed_4; -static PRIV_DECODE_FN poly_decode_signed_2; -static PRIV_DECODE_FN poly_decode_signed_4; +static ENCODE_FN poly_encode_signed_2; +static ENCODE_FN poly_encode_signed_4; +static ENCODE_FN poly_encode_signed_two_to_power_17; +static ENCODE_FN poly_encode_signed_two_to_power_19; +static DECODE_FN poly_decode_signed_2; +static DECODE_FN poly_decode_signed_4; +static DECODE_FN poly_decode_signed_two_to_power_17; +static DECODE_FN poly_decode_signed_two_to_power_19; -static ossl_inline int constant_time_declassify_int(int v) +/* Bit packing Algorithms */ + +/* + * Encodes a polynomial into a byte string, assuming that all coefficients are + * in the range 0..15 (4 bits). + * + * See FIPS 204, Algorithm 16, SimpleBitPack(w, b) where b = 4 bits + * + * i.e. Use 4 bits from each coefficient and pack them into bytes + * So every 2 coefficients fit into 1 byte. + * + * This is used to encode w1 when signing with ML-DSA-65 and ML-DSA-87 + * + * @param p A polynomial with coefficients all in the range (0..15) + * @param pkt A packet object to write 128 bytes to. + * + * @returns 1 on success, or 0 on error. + */ +static int poly_encode_4_bits(const POLY *p, WPACKET *pkt) { - return value_barrier_32(v); + uint8_t *out; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; + + if (!WPACKET_allocate_bytes(pkt, 32 * 4, &out)) + return 0; + + while (in < end) { + uint32_t z0 = *in++; + uint32_t z1 = *in++; + + *out++ = z0 | (z1 << 4); + } + return 1; } -/* Bit packing Algorithms */ +/* + * Encodes a polynomial into a byte string, assuming that all coefficients are + * in the range 0..43 (6 bits). + * + * See FIPS 204, Algorithm 16, SimpleBitPack(w, b) where b = 43 + * + * i.e. Use 6 bits from each coefficient and pack them into bytes + * So every 4 coefficients fit into 3 bytes. + * + * |c0||c1||c2||c3| + * | /| /\ / + * |6 2|4 4|2 6| + * + * This is used to encode w1 when signing with ML-DSA-44 + * + * @param p A polynomial with coefficients all in the range (0..43) + * @param pkt A packet object to write 96 bytes to. + * + * @returns 1 on success, or 0 on error. + */ +static int poly_encode_6_bits(const POLY *p, WPACKET *pkt) +{ + uint8_t *out; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; + + if (!WPACKET_allocate_bytes(pkt, 32 * 3, &out)) + return 0; + + while (in < end) { + uint32_t c0 = *in++; + uint32_t c1 = *in++; + uint32_t c2 = *in++; + uint32_t c3 = *in++; + + *out++ = c0 | (c1 << 6); + *out++ = c1 >> 4 | (c2 << 4); + *out++ = c3; + } + return 1; +} /* * Encodes a polynomial into a byte string, assuming that all coefficients are - * 10 bits. + * unsigned 10 bit values. * * See FIPS 204, Algorithm 16, SimpleBitPack(w, b) where b = 10 bits * @@ -46,12 +121,12 @@ static ossl_inline int constant_time_declassify_int(int v) * * @returns 1 on success, or 0 on error. */ -static int poly_encode_10_bits(WPACKET *pkt, const POLY *p) +static int poly_encode_10_bits(const POLY *p, WPACKET *pkt) { uint8_t *out; - const uint32_t *in = p->coeff, *end = in + 256; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; - if (!WPACKET_allocate_bytes(pkt, 5 * (256 / 4), &out)) + if (!WPACKET_allocate_bytes(pkt, 32 * 10, &out)) return 0; while (in < end) { @@ -73,26 +148,28 @@ static int poly_encode_10_bits(WPACKET *pkt, const POLY *p) * @brief Reverses the procedure of poly_encode_10_bits(). * See FIPS 204, Algorithm 18, SimpleBitUnpack(v, b) where b = 10. * - * @param pkt A packet object to read 320 bytes from. * @param p A polynomial to write coefficients to. + * @param pkt A packet object to read 320 bytes from. * * @returns 1 on success, or 0 on error. */ -static int poly_decode_10_bits(PACKET *pkt, POLY *p) +static int poly_decode_10_bits(POLY *p, PACKET *pkt) { - int i, ret = 0; + int ret = 0; const uint8_t *in = NULL; - uint32_t v, *out = p->coeff; + uint32_t v, mask = 0x3ff; /* 10 bits */ + uint32_t *out = p->coeff, *end = out + ML_DSA_NUM_POLY_COEFFICIENTS; - for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 4); i++) { + do { if (!PACKET_get_bytes(pkt, &in, 5)) goto err; - memcpy(&v, in, sizeof(v)); - *out++ = v & 0x3ff; - *out++ = (v >> 10) & 0x3ff; - *out++ = (v >> 20) & 0x3ff; + /* put first 4 bytes into v, 5th byte is accessed directly as in[4] */ + memcpy(&v, in, 4); + *out++ = v & mask; + *out++ = (v >> 10) & mask; + *out++ = (v >> 20) & mask; *out++ = (v >> 30) | (((uint32_t)in[4]) << 2); - } + } while (out < end); ret = 1; err: return ret; @@ -109,15 +186,15 @@ err: * This is used to encode the private key polynomial elements of s1 and s2 * for ML-DSA-65 (i.e. eta = 4) * - * @param pkt A packet to write 128 bytes of encoded polynomial coefficients to. * @param p An array of 256 coefficients all in the range -4..4 + * @param pkt A packet to write 128 bytes of encoded polynomial coefficients to. * * @returns 1 on success, or 0 on error. */ -static int poly_encode_signed_4(WPACKET *pkt, const POLY *p) +static int poly_encode_signed_4(const POLY *p, WPACKET *pkt) { uint8_t *out; - const uint32_t *in = p->coeff, *end = in + 256; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; if (!WPACKET_allocate_bytes(pkt, 32 * 4, &out)) return 0; @@ -135,23 +212,23 @@ static int poly_encode_signed_4(WPACKET *pkt, const POLY *p) * @brief Reverses the procedure of poly_encode_signed_4(). * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = b = 4. * - * @param pkt A packet object to read 128 bytes from. * @param p A polynomial to write coefficients to. + * @param pkt A packet object to read 128 bytes from. * * @returns 1 on success, or 0 on error. An error will occur if any of the * coefficients are not in the correct range. */ -static int poly_decode_signed_4(PACKET *pkt, POLY *s) +static int poly_decode_signed_4(POLY *p, PACKET *pkt) { int i, ret = 0; - uint32_t v, *out = s->coeff; + uint32_t v, *out = p->coeff; const uint8_t *in; uint32_t msbs, mask; for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) { if (!PACKET_get_bytes(pkt, &in, 4)) goto err; - memcpy(&v, &in, 4); + memcpy(&v, in, 4); /* * None of the nibbles may be >= 9. So if the MSB of any nibble is set, @@ -164,7 +241,7 @@ static int poly_decode_signed_4(PACKET *pkt, POLY *s) * A nibble is only out of range in the case of invalid input, in which case * it is okay to leak the value. */ - if (constant_time_declassify_int((mask & v) != 0)) + if (value_barrier_32((mask & v) != 0)) goto err; *out++ = mod_sub(4, v & 15); @@ -198,15 +275,15 @@ static int poly_decode_signed_4(PACKET *pkt, POLY *s) * | / / | | / / | | / * |3 3 2| 1 3 3 1| 2 3 3| * - * @param pkt A packet to write 64 bytes of encoded polynomial coefficients to. * @param p An array of 256 coefficients all in the range -2..2 + * @param pkt A packet to write 64 bytes of encoded polynomial coefficients to. * * @returns 1 on success, or 0 on error. */ -static int poly_encode_signed_2(WPACKET *pkt, const POLY *s) +static int poly_encode_signed_2(const POLY *p, WPACKET *pkt) { uint8_t *out; - const uint32_t *in = s->coeff, *end = in + 256; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; if (!WPACKET_allocate_bytes(pkt, 32 * 3, &out)) return 0; @@ -232,13 +309,13 @@ static int poly_encode_signed_2(WPACKET *pkt, const POLY *s) * @brief Reverses the procedure of poly_encode_signed_2(). * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = b = 2. * - * @param pkt A packet object to read 64 encoded bytes from. * @param p A polynomial to write coefficients to. + * @param pkt A packet object to read 64 encoded bytes from. * * @returns 1 on success, or 0 on error. An error will occur if any of the * coefficients are not in the correct range. */ -static int poly_decode_signed_2(PACKET *pkt, POLY *p) +static int poly_decode_signed_2(POLY *p, PACKET *pkt) { int i, ret = 0; uint32_t v = 0, *out = p->coeff; @@ -248,7 +325,7 @@ static int poly_decode_signed_2(PACKET *pkt, POLY *p) for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) { if (!PACKET_get_bytes(pkt, &in, 3)) goto err; - memcpy(&v, &in, 3); + memcpy(&v, in, 3); /* * Each octal value (3 bits) must be <= 4, So if the MSB is set then the * bottom 2 bits must not be set. @@ -261,7 +338,7 @@ static int poly_decode_signed_2(PACKET *pkt, POLY *p) * A nibble is only out of range in the case of invalid input, in which * case it is okay to leak the value. */ - if (constant_time_declassify_int((mask & v) != 0)) + if (value_barrier_32((mask & v) != 0)) goto err; *out++ = mod_sub(2, v & 7); @@ -293,16 +370,16 @@ static int poly_decode_signed_2(PACKET *pkt, POLY *p) * | | | | / \ | | | | * |13 13 13 13 12 |1 13 13 13 24 * + * @param p An array of 256 coefficients all in the range -2^12+1..2^12 * @param pkt A packet to write 416 (13 * 256 / 8) bytes of encoded polynomial * coefficients to. - * @param p An array of 256 coefficients all in the range -2^12+1..2^12 * * @returns 1 on success, or 0 on error. */ -static int poly_encode_signed_two_to_power_12(WPACKET *pkt, const POLY *p) +static int poly_encode_signed_two_to_power_12(const POLY *p, WPACKET *pkt) { static const uint32_t range = 1u << 12; - const uint32_t *in = p->coeff, *end = in + 256; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; while (in < end) { uint64_t z0 = mod_sub(range, *in++); /* < 2^13 */ @@ -327,12 +404,12 @@ static int poly_encode_signed_two_to_power_12(WPACKET *pkt, const POLY *p) * @brief Reverses the procedure of poly_encode_signed_two_to_power_12(). * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = 2^12 - 1, b = 2^12. * - * @param pkt A packet object to read 416 encoded bytes from. * @param p A polynomial to write coefficients to. + * @param pkt A packet object to read 416 encoded bytes from. * * @returns 1 on success, or 0 on error. */ -static int poly_decode_signed_two_to_power_12(PACKET *pkt, POLY *p) +static int poly_decode_signed_two_to_power_12(POLY *p, PACKET *pkt) { int i, ret = 0; uint64_t a1 = 0, a2 = 0; @@ -347,10 +424,6 @@ static int poly_decode_signed_two_to_power_12(PACKET *pkt, POLY *p) memcpy(&a1, in, 8); memcpy(&a2, in + 8, 5); - /* - * It is not possible for a 13-bit number to be out of range when the - * max is 2^12. - */ *out++ = mod_sub(range, a1 & mask_13_bits); *out++ = mod_sub(range, (a1 >> 13) & mask_13_bits); *out++ = mod_sub(range, (a1 >> 26) & mask_13_bits); @@ -365,6 +438,165 @@ static int poly_decode_signed_two_to_power_12(PACKET *pkt, POLY *p) return ret; } +/* + * @brief Encodes a polynomial into a byte string, assuming that all + * coefficients are in the range (-2^19 + 1)..2^19. + * See FIPS 204, Algorithm 17, BitPack(w, a, b). where a = 2^19 - 1, b = 2^19. + * + * This is used to encode signatures for ML-DSA-65 & ML-DSA-87 (gamma1 = 2^19) + * + * Use 20 bits from each coefficient and pack them into bytes + * + * The code below packs every 4 (20 bit) coefficients into 10 bytes + * z0 z1 z2 z3 + * | |\ | | \ + * |20 12|8 20 4|16 + * + * @param p An array of 256 coefficients all in the range -2^19+1..2^19 + * @param pkt A packet to write 640 (20 * 256 / 8) bytes of encoded polynomial + * coefficients to. + * + * @returns 1 on success, or 0 on error. + */ +static int poly_encode_signed_two_to_power_19(const POLY *p, WPACKET *pkt) +{ + static const uint32_t range = 1u << 19; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; + + while (in < end) { + uint32_t z0 = mod_sub(range, *in++); /* < 2^20 */ + uint32_t z1 = mod_sub(range, *in++); + uint32_t z2 = mod_sub(range, *in++); + uint32_t z3 = mod_sub(range, *in++); + + z0 |= (z1 << 20); + z1 >>= 12; + z1 |= (z2 << 8) | (z3 << 28); + z3 >>= 4; + + if (!WPACKET_memcpy(pkt, &z0, sizeof(z0)) + || !WPACKET_memcpy(pkt, &z1, sizeof(z1)) + || !WPACKET_memcpy(pkt, &z3, 2)) + return 0; + } + return 1; +} + +/* + * @brief Reverses the procedure of poly_encode_signed_two_to_power_19(). + * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = 2^19 - 1, b = 2^19. + * + * @param p A polynomial to write coefficients to. + * @param pkt A packet object to read 640 encoded bytes from. + * + * @returns 1 on success, or 0 on error. + */ +static int poly_decode_signed_two_to_power_19(POLY *p, PACKET *pkt) +{ + int i, ret = 0; + uint32_t a1, a2, a3 = 0; + uint32_t *out = p->coeff; + const uint8_t *in; + static const uint32_t range = 1u << 19; + static const uint32_t mask_20_bits = (1u << 20) - 1; + + for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 4); i++) { + if (!PACKET_get_bytes(pkt, &in, 10)) + goto err; + memcpy(&a1, in, 4); + memcpy(&a2, in + 4, 4); + memcpy(&a3, in + 8, 2); + + *out++ = mod_sub(range, a1 & mask_20_bits); + *out++ = mod_sub(range, (a1 >> 20) | ((a2 & 0xFF) << 12)); + *out++ = mod_sub(range, (a2 >> 8) & mask_20_bits); + *out++ = mod_sub(range, (a2 >> 28) | (a3 << 4)); + } + ret = 1; + err: + return ret; +} + +/* + * @brief Encodes a polynomial into a byte string, assuming that all + * coefficients are in the range (-2^17 + 1)..2^17. + * See FIPS 204, Algorithm 17, BitPack(w, a, b). where a = 2^17 - 1, b = 2^17. + * + * This is used to encode signatures for ML-DSA-44 (where gamma1 = 2^17) + * + * Use 18 bits from each coefficient and pack them into bytes + * + * The code below packs every 4 (18 bit) coefficients into 9 bytes + * z0 z1 z2 z3 + * | |\ | | \ + * |18 14|4 18 10| 8 + * + * @param p An array of 256 coefficients all in the range -2^17+1..2^17 + * @param pkt A packet to write 576 (18 * 256 / 8) bytes of encoded polynomial + * coefficients to. + * + * @returns 1 on success, or 0 on error. + */ +static int poly_encode_signed_two_to_power_17(const POLY *p, WPACKET *pkt) +{ + static const uint32_t range = 1u << 17; + const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; + + while (in < end) { + uint32_t z0 = mod_sub(range, *in++); /* < 2^18 */ + uint32_t z1 = mod_sub(range, *in++); + uint32_t z2 = mod_sub(range, *in++); + uint32_t z3 = mod_sub(range, *in++); + + z0 |= (z1 << 18); + z1 >>= 14; + z1 |= (z2 << 4) | (z3 << 22); + z3 >>= 10; + + if (!WPACKET_memcpy(pkt, &z0, sizeof(z0)) + || !WPACKET_memcpy(pkt, &z1, sizeof(z1)) + || !WPACKET_memcpy(pkt, &z3, 1)) + return 0; + } + return 1; +} + +/* + * @brief Reverses the procedure of poly_encode_signed_two_to_power_17(). + * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = 2^17 - 1, b = 2^17. + * + * @param p A polynomial to write coefficients to. + * @param pkt A packet object to read 576 encoded bytes from. + * + * @returns 1 on success, or 0 on error. + */ +static int poly_decode_signed_two_to_power_17(POLY *p, PACKET *pkt) +{ + int ret = 0; + uint32_t a1, a2, a3 = 0; + uint32_t *out = p->coeff; + const uint32_t *end = out + ML_DSA_NUM_POLY_COEFFICIENTS; + const uint8_t *in; + static const uint32_t range = 1u << 17; + static const uint32_t mask_18_bits = (1u << 18) - 1; + + while (out < end) { + if (!PACKET_get_bytes(pkt, &in, 10)) + goto err; + memcpy(&a1, in, 4); + memcpy(&a2, in + 4, 4); + memcpy(&a3, in + 8, 1); + + *out++ = mod_sub(range, a1 & mask_18_bits); + *out++ = mod_sub(range, (a1 >> 18) | ((a2 & 0xF) << 14)); + *out++ = mod_sub(range, (a2 >> 4) & mask_18_bits); + *out++ = mod_sub(range, (a2 >> 22) | (a3 << 10)); + } + ret = 1; + err: + return ret; +} + /* * @brief Encode the public key as an array of bytes. * See FIPS 204, Algorithm 22, pkEncode(). @@ -390,7 +622,7 @@ int ossl_ml_dsa_pk_encode(ML_DSA_KEY *key) || !WPACKET_memcpy(&pkt, key->rho, sizeof(key->rho))) goto err; for (i = 0; i < t1_len; i++) - if (!poly_encode_10_bits(&pkt, t1 + i)) + if (!poly_encode_10_bits(t1 + i, &pkt)) goto err; OPENSSL_free(key->pub_encoding); key->pub_encoding = enc; @@ -412,7 +644,7 @@ err: * * @returns 1 if the public key was decoded successfully or 0 otherwise. */ -int ossl_ml_dsa_pk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key) +int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len) { int ret = 0; size_t i; @@ -425,7 +657,7 @@ int ossl_ml_dsa_pk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key) || PACKET_copy_bytes(&pkt, key->rho, sizeof(key->rho))) goto err; for (i = 0; i < key->t1.num_poly; i++) - if (!poly_decode_10_bits(&pkt, &key->t1.poly[i])) + if (!poly_decode_10_bits(key->t1.poly + i, &pkt)) goto err; memcpy(key->pub_encoding, in, in_len); ret = 1; @@ -446,7 +678,7 @@ int ossl_ml_dsa_sk_encode(ML_DSA_KEY *key) int ret = 0; const ML_DSA_PARAMS *params = key->params; size_t i, k = params->k, l = params->l; - PRIV_ENCODE_FN *encode_fn; + ENCODE_FN *encode_fn; size_t enc_len = params->sk_len; const POLY *t0 = key->t0.poly; WPACKET pkt; @@ -456,7 +688,7 @@ int ossl_ml_dsa_sk_encode(ML_DSA_KEY *key) return 0; /* eta is the range of private key coefficients (-eta...eta) */ - if (params->eta == 4) + if (params->eta == ML_DSA_ETA_4) encode_fn = poly_encode_signed_4; else encode_fn = poly_encode_signed_2; @@ -467,13 +699,13 @@ int ossl_ml_dsa_sk_encode(ML_DSA_KEY *key) || !WPACKET_memcpy(&pkt, key->tr, sizeof(key->tr))) goto err; for (i = 0; i < l; ++i) - if (!encode_fn(&pkt, &key->s1.poly[i])) + if (!encode_fn(key->s1.poly + i, &pkt)) goto err; for (i = 0; i < k; ++i) - if (!encode_fn(&pkt, &key->s2.poly[i])) + if (!encode_fn(key->s2.poly + i, &pkt)) goto err; - for (i = 0; i < k; ++i, t0++) - if (!poly_encode_signed_two_to_power_12(&pkt, t0)) + for (i = 0; i < k; ++i) + if (!poly_encode_signed_two_to_power_12(t0++, &pkt)) goto err; OPENSSL_clear_free(key->priv_encoding, enc_len); key->priv_encoding = enc; @@ -495,11 +727,11 @@ err: * * @returns 1 if the private key was decoded successfully or 0 otherwise. */ -int ossl_ml_dsa_sk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key) +int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len) { int ret = 0; uint8_t *enc = NULL; - PRIV_DECODE_FN *decode_fn; + DECODE_FN *decode_fn; const ML_DSA_PARAMS *params = key->params; size_t i, k = params->k, l = params->l; PACKET pkt; @@ -511,7 +743,7 @@ int ossl_ml_dsa_sk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key) return 0; /* eta is the range of private key coefficients (-eta...eta) */ - if (params->eta == 4) + if (params->eta == ML_DSA_ETA_4) decode_fn = poly_decode_signed_4; else decode_fn = poly_decode_signed_2; @@ -523,13 +755,13 @@ int ossl_ml_dsa_sk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key) goto err; for (i = 0; i < l; ++i) - if (!decode_fn(&pkt, key->s1.poly + i)) + if (!decode_fn(key->s1.poly + i, &pkt)) goto err; for (i = 0; i < k; ++i) - if (!decode_fn(&pkt, key->s2.poly + i)) + if (!decode_fn(key->s2.poly + i, &pkt)) goto err; for (i = 0; i < k; ++i) - if (!poly_decode_signed_two_to_power_12(&pkt, key->t0.poly + i)) + if (!poly_decode_signed_two_to_power_12(key->t0.poly + i, &pkt)) goto err; if (PACKET_remaining(&pkt) != 0) goto err; @@ -539,3 +771,196 @@ int ossl_ml_dsa_sk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key) err: return ret; } + +/* + * See FIPS 204, Algorithm 20, HintBitPack(). + * Hint is composed of k polynomials with binary coefficients where only 'omega' + * of all the coefficients are set to 1. + * This can be encoded as a byte array of 'omega' polynomial coefficient index + * positions for the coefficients that are set, followed by + * k values of the last coefficient index used in each polynomial. + */ +static int hint_bits_encode(const VECTOR *hint, WPACKET *pkt, uint32_t omega) +{ + int i, j, k = hint->num_poly; + size_t coeff_index = 0; + POLY *p = hint->poly; + uint8_t *data; + + if (!WPACKET_allocate_bytes(pkt, omega + k, &data)) + return 0; + + for (i = 0; i < k; i++, p++) { + for (j = 0; j < ML_DSA_NUM_POLY_COEFFICIENTS; j++) + if (p->coeff[j] != 0) { + assert(coeff_index < omega); + data[coeff_index++] = j; + } + data[omega + i] = (uint8_t)coeff_index; + } + return 1; +} + +/* + * @brief Reverse the process of hint_bits_encode() + * See FIPS 204, Algorithm 21, HintBitUnpack() + * + * @returns 1 if the hints were successfully unpacked, or 0 + * if 'pkt' is too small or malformed. + */ +static int hint_bits_decode(VECTOR *hint, PACKET *pkt, uint32_t omega) +{ + size_t coeff_index = 0, k = hint->num_poly; + const uint8_t *in, *limits; + POLY *p = hint->poly, *end = p + k; + + if (!PACKET_get_bytes(pkt, &in, omega) + || !PACKET_get_bytes(pkt, &limits, k)) + return 0; + + vector_zero(hint); /* Set all coefficients to zero */ + + do { + const uint32_t limit = *limits++; + int last = -1; + + if (limit < coeff_index || limit > omega) + return 0; + + while (coeff_index < limit) { + int byte = in[coeff_index++]; + + if (last >= 0 && byte <= last) + return 0; + last = byte; + p->coeff[byte] = 1; + } + } while (++p < end); + + for (; coeff_index < omega; coeff_index++) + if (in[coeff_index] != 0) + return 0; + return 1; +} + +/* + * @brief Encode a ML_DSA signature as an array of bytes. + * See FIPS 204, Algorithm 26, sigEncode(). + * + * @param + * @param + * @returns 1 if the signature was encoded successfully or 0 otherwise. + */ +int ossl_ml_dsa_sig_encode(const ML_DSA_SIG *sig, const ML_DSA_PARAMS *params, + uint8_t *out) +{ + int ret = 0; + size_t i; + ENCODE_FN *encode_fn; + WPACKET pkt; + + if (out == NULL) + return 0; + + if (params->gamma1 == ML_DSA_GAMMA1_TWO_POWER_19) + encode_fn = poly_encode_signed_two_to_power_19; + else + encode_fn = poly_encode_signed_two_to_power_17; + + if (!WPACKET_init_static_len(&pkt, out, params->sig_len, 0) + || !WPACKET_memcpy(&pkt, sig->c_tilde, sig->c_tilde_len)) + goto err; + + for (i = 0; i < sig->z.num_poly; ++i) + if (!encode_fn(sig->z.poly + i, &pkt)) + goto err; + if (!hint_bits_encode(&sig->hint, &pkt, params->omega)) + goto err; + ret = 1; +err: + WPACKET_finish(&pkt); + return ret; +} + +/* + * @param sig is a initialized signature object to decode into. + * @param in An encoded signature + * @param in_len The size of |in| + * @param params contains constants for an ML-DSA algorithm (such as gamma1) + * @returns 1 if the signature was successfully decoded or 0 otherwise. + */ +int ossl_ml_dsa_sig_decode(ML_DSA_SIG *sig, const uint8_t *in, size_t in_len, + const ML_DSA_PARAMS *params) +{ + int ret = 0; + size_t i; + DECODE_FN *decode_fn; + PACKET pkt; + + if (params->gamma1 == ML_DSA_GAMMA1_TWO_POWER_19) + decode_fn = poly_decode_signed_two_to_power_19; + else + decode_fn = poly_decode_signed_two_to_power_17; + + if (!PACKET_buf_init(&pkt, in, in_len) + || !PACKET_copy_bytes(&pkt, sig->c_tilde, sig->c_tilde_len)) + goto err; + for (i = 0; i < sig->z.num_poly; ++i) + if (!decode_fn(sig->z.poly + i, &pkt)) + goto err; + + if (!hint_bits_decode(&sig->hint, &pkt, params->omega) + || PACKET_remaining(&pkt) != 0) + goto err; + ret = 1; +err: + return ret; +} + +int ossl_ml_dsa_poly_decode_expand_mask(POLY *out, + const uint8_t *in, size_t in_len, + uint32_t gamma1) +{ + PACKET pkt; + + if (!PACKET_buf_init(&pkt, in, in_len)) + return 0; + if (gamma1 == ML_DSA_GAMMA1_TWO_POWER_19) + return poly_decode_signed_two_to_power_19(out, &pkt); + else + return poly_decode_signed_two_to_power_17(out, &pkt); +} + +/* + * @brief Encode a polynomial vector as an array of bytes. + * Where the polynomial coefficients have a range of [0..15] or [0..43] + * depending on the value of gamma2. + * + * See FIPS 204, Algorithm 28, w1Encode(). + * + * @param w1 The vector to convert to bytes + * @param gamma2 either ML_DSA_GAMMA2_Q_MINUS1_DIV32 or ML_DSA_GAMMA2_Q_MINUS1_DIV88 + * @returns 1 if the signature was encoded successfully or 0 otherwise. + */ +int ossl_ml_dsa_w1_encode(const VECTOR *w1, uint32_t gamma2, + uint8_t *out, size_t out_len) +{ + WPACKET pkt; + ENCODE_FN *encode_fn; + int ret = 0; + size_t i; + + if (!WPACKET_init_static_len(&pkt, out, out_len, 0)) + return 0; + if (gamma2 == ML_DSA_GAMMA2_Q_MINUS1_DIV32) + encode_fn = poly_encode_4_bits; + else + encode_fn = poly_encode_6_bits; + for (i = 0; i < w1->num_poly; ++i) + if (!encode_fn(w1->poly + i, &pkt)) + goto err; + ret = 1; +err: + WPACKET_finish(&pkt); + return ret; +} diff --git a/crypto/ml_dsa/ml_dsa_hash.h b/crypto/ml_dsa/ml_dsa_hash.h new file mode 100644 index 00000000000..532d9e557f0 --- /dev/null +++ b/crypto/ml_dsa/ml_dsa_hash.h @@ -0,0 +1,41 @@ +/* + * 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 + +static ossl_inline ossl_unused int +shake_xof(EVP_MD_CTX *ctx, const uint8_t *in, size_t in_len, + uint8_t *out, size_t out_len) +{ + return (EVP_DigestInit_ex2(ctx, NULL, NULL) == 1 + && EVP_DigestUpdate(ctx, in, in_len) == 1 + && EVP_DigestSqueeze(ctx, out, out_len) == 1); +} + +static ossl_inline ossl_unused int +shake_xof_2(EVP_MD_CTX *ctx, const uint8_t *in1, size_t in1_len, + const uint8_t *in2, size_t in2_len, uint8_t *out, size_t out_len) +{ + return EVP_DigestInit_ex2(ctx, NULL, NULL) + && EVP_DigestUpdate(ctx, in1, in1_len) + && EVP_DigestUpdate(ctx, in2, in2_len) + && EVP_DigestSqueeze(ctx, out, out_len); +} + +static ossl_inline ossl_unused int +shake_xof_3(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, uint8_t *out, size_t out_len) +{ + return EVP_DigestInit_ex2(ctx, NULL, NULL) + && EVP_DigestUpdate(ctx, in1, in1_len) + && EVP_DigestUpdate(ctx, in2, in2_len) + && EVP_DigestUpdate(ctx, in3, in3_len) + && EVP_DigestSqueeze(ctx, out, out_len); +} diff --git a/crypto/ml_dsa/ml_dsa_key.c b/crypto/ml_dsa/ml_dsa_key.c index f8a488f0d1d..57eb599507f 100644 --- a/crypto/ml_dsa/ml_dsa_key.c +++ b/crypto/ml_dsa/ml_dsa_key.c @@ -15,6 +15,7 @@ #include "ml_dsa_key.h" #include "ml_dsa_params.h" #include "ml_dsa_matrix.h" +#include "ml_dsa_hash.h" /** * @brief Create a new ML_DSA_KEY object @@ -26,15 +27,15 @@ ML_DSA_KEY *ossl_ml_dsa_key_new(OSSL_LIB_CTX *libctx, const char *alg) { ML_DSA_KEY *ret; - size_t sz; + size_t poly_sz; const ML_DSA_PARAMS *params = ossl_ml_dsa_params_get(alg); POLY *poly; if (params == NULL) return NULL; - sz = sizeof(POLY) * (params->k * 3 + params->l); - ret = OPENSSL_zalloc(sizeof(*ret) + sz); + poly_sz = sizeof(POLY) * (params->k * 3 + params->l); + ret = OPENSSL_zalloc(sizeof(*ret) + poly_sz); if (ret != NULL) { if (!CRYPTO_NEW_REF(&ret->references, 1)) { OPENSSL_free(ret); @@ -105,21 +106,26 @@ int ossl_ml_dsa_key_equal(const ML_DSA_KEY *key1, const ML_DSA_KEY *key2, { if (key1->params != key2->params) return 0; - if (key1->pub_encoding != NULL) { - if (key2->pub_encoding == NULL - || memcmp(key1->pub_encoding, key1->pub_encoding, - key1->params->pk_len) != 0) - return 0; - } else if (key2->pub_encoding != NULL) { - return 0; - } - if (key1->priv_encoding != NULL) { - if (key2->priv_encoding == NULL - || memcmp(key1->priv_encoding, key1->priv_encoding, - key1->params->sk_len) != 0) + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (key1->pub_encoding != NULL) { + if (key2->pub_encoding == NULL + || memcmp(key1->pub_encoding, key2->pub_encoding, + key1->params->pk_len) != 0) + return 0; + } else if (key2->pub_encoding != NULL) { + return 0; + } + } + if (key1->priv_encoding != NULL) { + if (key2->priv_encoding == NULL + || memcmp(key1->priv_encoding, key2->priv_encoding, + key1->params->sk_len) != 0) + return 0; + } else if (key2->priv_encoding != NULL) { return 0; - } else if (key2->priv_encoding != NULL) { - return 0; + } } return 1; } @@ -156,14 +162,14 @@ int ossl_ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[], p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); if (p != NULL) { if (p->data_type != OSSL_PARAM_OCTET_STRING - || !ossl_ml_dsa_sk_decode(p->data, p->data_size, key)) + || !ossl_ml_dsa_sk_decode(key, p->data, p->data_size)) return 0; } } p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); if (p != NULL) { if (p->data_type != OSSL_PARAM_OCTET_STRING - || !ossl_ml_dsa_pk_decode(p->data, p->data_size, key)) + || !ossl_ml_dsa_pk_decode(key, p->data, p->data_size)) return 0; } return 1; @@ -181,28 +187,33 @@ int ossl_ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[], * of the uncompressed public key polynomial t. * @returns 1 on success, or 0 on failure. */ -static int public_from_private(ML_DSA_CTX *ctx, const ML_DSA_KEY *key, +static int public_from_private(const ML_DSA_KEY *key, EVP_MD_CTX *g_ctx, VECTOR *t1, VECTOR *t0) { - const ML_DSA_PARAMS *params = ctx->params; - POLY polys[ML_DSA_K_MAX + ML_DSA_L_MAX + ML_DSA_K_MAX * ML_DSA_L_MAX]; + const ML_DSA_PARAMS *params = key->params; + uint32_t k = params->k, l = params->l; + POLY *polys; MATRIX a_ntt; VECTOR s1_ntt; VECTOR t; - vector_init(&t, polys, params->k); - vector_init(&s1_ntt, polys + params->k, params->l); - matrix_init(&a_ntt, polys + params->k + params->l, params->k, params->l); + polys = OPENSSL_malloc(sizeof(*polys) * (k + l + k * l)); + if (polys == NULL) + return 0; + + vector_init(&t, polys, k); + vector_init(&s1_ntt, t.poly + k, l); + matrix_init(&a_ntt, s1_ntt.poly + l, k, l); /* Using rho generate A' = A in NTT form */ - if (!ossl_ml_dsa_sample_expandA(ctx->g_ctx, key->rho, &a_ntt)) - return 0; + if (!matrix_expand_A(g_ctx, key->rho, &a_ntt)) + goto err; /* t = NTT_inv(A' * NTT(s1)) + s2 */ vector_copy(&s1_ntt, &key->s1); vector_ntt(&s1_ntt); - ossl_ml_dsa_matrix_mult_vector(&a_ntt, &s1_ntt, &t); + matrix_mult_vector(&a_ntt, &s1_ntt, &t); vector_ntt_inverse(&t); vector_add(&t, &key->s2, &t); @@ -211,6 +222,8 @@ static int public_from_private(ML_DSA_CTX *ctx, const ML_DSA_KEY *key, /* Zeroize secret */ vector_zero(&s1_ntt); +err: + OPENSSL_free(polys); return 1; } @@ -219,32 +232,42 @@ int ossl_ml_dsa_key_pairwise_check(const ML_DSA_KEY *key) int ret = 0; ML_DSA_CTX *ctx = NULL; VECTOR t1, t0; - POLY polys[ML_DSA_K_MAX * 2]; + POLY *polys = NULL; + uint32_t k = key->params->k; if (key->pub_encoding == NULL || key->priv_encoding == 0) return 0; + polys = OPENSSL_malloc(sizeof(*polys) * (2 * k)); + if (polys == NULL) + return 0; + ctx = ossl_ml_dsa_ctx_new(key->params->alg, key->libctx, key->propq); if (ctx == NULL) return 0; - vector_init(&t1, polys, key->params->k); - vector_init(&t0, polys + key->params->k, key->params->k); - if (!public_from_private(ctx, key, &t1, &t0)) + vector_init(&t1, polys, k); + vector_init(&t0, polys + k, k); + if (!public_from_private(key, ctx->g_ctx, &t1, &t0)) goto err; ret = vector_equal(&t1, &key->t1) && vector_equal(&t0, &key->t0); err: ossl_ml_dsa_ctx_free(ctx); + OPENSSL_free(polys); return ret; } -static int shake_xof(EVP_MD_CTX *ctx, const uint8_t *in, size_t in_len, - uint8_t *out, size_t out_len) +int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key, ML_DSA_CTX *ctx) { - return (EVP_DigestInit_ex2(ctx, NULL, NULL) == 1 - && EVP_DigestUpdate(ctx, in, in_len) == 1 - && EVP_DigestFinalXOF(ctx, out, out_len) == 1); + if (key->pub_encoding != NULL) + return 1; + if (key->priv_encoding == NULL) + return 0; + return public_from_private(key, ctx->g_ctx, &key->t1, &key->t0) + && ossl_ml_dsa_pk_encode(key) + && shake_xof(ctx->h_ctx, key->pub_encoding, key->params->pk_len, + key->tr, sizeof(key->tr)); } /* @@ -280,9 +303,8 @@ static int keygen_internal(ML_DSA_CTX *ctx, const uint8_t *seed, size_t seed_len memcpy(out->rho, rho, sizeof(out->rho)); memcpy(out->K, K, sizeof(out->K)); - ret = ossl_ml_dsa_sample_expandS(ctx->h_ctx, params->eta, priv_seed, - &out->s1, &out->s2) - && public_from_private(ctx, out, &out->t1, &out->t0) + ret = vector_expand_S(ctx->h_ctx, params->eta, priv_seed, &out->s1, &out->s2) + && public_from_private(out, ctx->g_ctx, &out->t1, &out->t0) && ossl_ml_dsa_pk_encode(out) && shake_xof(ctx->h_ctx, out->pub_encoding, out->params->pk_len, out->tr, sizeof(out->tr)) @@ -305,8 +327,8 @@ int ossl_ml_dsa_generate_key(ML_DSA_CTX *ctx, OSSL_LIB_CTX *lib_ctx, return 0; if (entropy != NULL && entropy_len != 0) { - if (entropy_len < seed_len) - goto err; + if (entropy_len != seed_len) + return 0; memcpy(seed, entropy, seed_len); } else { if (RAND_priv_bytes_ex(lib_ctx, seed, seed_len, 0) <= 0) @@ -347,7 +369,7 @@ size_t ossl_ml_dsa_key_get_pub_len(const ML_DSA_KEY *key) size_t ossl_ml_dsa_key_get_collision_strength_bits(const ML_DSA_KEY *key) { - return key->params->strength; + return key->params->bit_strength; } /* Returns the private key data or NULL if there is no private key */ diff --git a/crypto/ml_dsa/ml_dsa_key_compress.c b/crypto/ml_dsa/ml_dsa_key_compress.c index 035475b885a..ffeb2b43e45 100644 --- a/crypto/ml_dsa/ml_dsa_key_compress.c +++ b/crypto/ml_dsa/ml_dsa_key_compress.c @@ -64,7 +64,7 @@ uint32_t ossl_ml_dsa_key_compress_high_bits(uint32_t r, uint32_t gamma2) uint32_t r1 = (r + 127) >> 7; /* TODO - figure out what this is doing */ - if (gamma2 == ML_DSA_Q_MINUS1_DIV32) { + if (gamma2 == ML_DSA_GAMMA2_Q_MINUS1_DIV32) { r1 = (r1 * 1025 + (1 << 21)) >> 22; r1 &= 15; /* mod 16 */ return r1; diff --git a/crypto/ml_dsa/ml_dsa_local.h b/crypto/ml_dsa/ml_dsa_local.h index 54e1d3b35a7..ea9d92348e8 100644 --- a/crypto/ml_dsa/ml_dsa_local.h +++ b/crypto/ml_dsa/ml_dsa_local.h @@ -12,28 +12,49 @@ # include "crypto/ml_dsa.h" # include "internal/constant_time.h" +# include "internal/packet.h" -/* Maximimum size of the 'A' matrix */ -# define ML_DSA_K_MAX 8 -# define ML_DSA_L_MAX 7 - +/* The following constants are shared by ML-DSA-44, ML-DSA-65 & ML-DSA-87 */ # define ML_DSA_SEED_BYTES 32 # define ML_DSA_Q 8380417 /* The modulus is 23 bits (2^23 - 2^13 + 1) */ # define ML_DSA_Q_MINUS1_DIV2 ((ML_DSA_Q - 1) / 2) -# define ML_DSA_Q_MINUS1_DIV32 ((ML_DSA_Q - 1) / 32) + # define ML_DSA_Q_BITS 23 # define ML_DSA_Q_INV 58728449 /* q^-1 satisfies: q^-1 * q = 1 mod 2^32 */ # define ML_DSA_Q_NEG_INV 4236238847 /* Inverse of -q modulo 2^32 */ # define ML_DSA_DEGREE_INV_MONTGOMERY 41978 /* Inverse of 256 mod q, in Montgomery form. */ -# define ML_DSA_D_BITS 13 /* The number of bits dropped from t */ +# define ML_DSA_D_BITS 13 /* The number of bits dropped from the public vector t */ # define ML_DSA_NUM_POLY_COEFFICIENTS 256 /* The number of coefficients in the polynomials */ # define ML_DSA_RHO_BYTES 32 /* p = Public Random Seed */ # define ML_DSA_PRIV_SEED_BYTES 64 /* p' = Private random seed */ # define ML_DSA_K_BYTES 32 /* K = Private random seed for signing */ -# define ML_DSA_TR_BYTES 64 /* Hash of public key used for signing */ -# define ML_DSA_MU_BYTES 64 -# define ML_DSA_RHO_PRIME_BYTES 64 +# define ML_DSA_TR_BYTES 64 /* Size of the Hash of the public key used for signing */ +# define ML_DSA_MU_BYTES 64 /* Size of the Hash for the message representative */ +# define ML_DSA_RHO_PRIME_BYTES 64 /* private random seed size */ + +/* + * There is special case code related to encoding/decoding that tests the + * for the following values. + */ +/* + * The possible value for eta - If a new value is added, then all code + * that accesses ML_DSA_ETA_4 would need to be modified. + */ +# define ML_DSA_ETA_4 4 +# define ML_DSA_ETA_2 2 +/* + * The possible values of gamma1 - If a new value is added, then all code + * that accesses ML_DSA_GAMMA1_TWO_POWER_19 would need to be modified. + */ +# define ML_DSA_GAMMA1_TWO_POWER_19 (1 << 19) +# define ML_DSA_GAMMA1_TWO_POWER_17 (1 << 17) +/* + * The possible values for gamma2 - If a new value is added, then all code + * that accesses ML_DSA_GAMMA2_Q_MINUS1_DIV32 would need to be modified. + */ +# define ML_DSA_GAMMA2_Q_MINUS1_DIV32 ((ML_DSA_Q - 1) / 32) +# define ML_DSA_GAMMA2_Q_MINUS1_DIV88 ((ML_DSA_Q - 1) / 88) typedef struct ml_dsa_params_st ML_DSA_PARAMS; typedef struct poly_st POLY; @@ -47,7 +68,8 @@ typedef struct matrix_st MATRIX; * - OpenSSL also uses pre-fetched EVP_MD_CTX objects for Hashing purposes. * * ML_DSA_CTX is a container to hold all these objects. This object is - * resolved early and is then passed to most ML-DSA related functions. + * resolved early and can then be used to pass these values to + * most ML-DSA related functions. */ struct ml_dsa_ctx_st { const ML_DSA_PARAMS *params; @@ -55,9 +77,19 @@ struct ml_dsa_ctx_st { EVP_MD_CTX *g_ctx; /* SHAKE-128 */ }; -int ossl_ml_dsa_sample_expandA(EVP_MD_CTX *g_ctx, const uint8_t *rho, MATRIX *out); -int ossl_ml_dsa_sample_expandS(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, - VECTOR *s1, VECTOR *s2); +typedef struct ml_dsa_sig_st ML_DSA_SIG; + +int ossl_ml_dsa_matrix_expand_A(EVP_MD_CTX *g_ctx, const uint8_t *rho, MATRIX *out); +int ossl_ml_dsa_vector_expand_S(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, + VECTOR *s1, VECTOR *s2); +void ossl_ml_dsa_matrix_mult_vector(const MATRIX *matrix_kl, const VECTOR *vl, + VECTOR *vk); +int ossl_ml_dsa_poly_expand_mask(POLY *out, + const uint8_t *seed, size_t seed_len, + uint32_t gamma1, EVP_MD_CTX *h_ctx); +int ossl_ml_dsa_poly_sample_in_ball(POLY *out_c, const uint8_t *seed, int seed_len, + EVP_MD_CTX *h_ctx, uint32_t tau); + void ossl_ml_dsa_poly_ntt(POLY *s); void ossl_ml_dsa_poly_ntt_inverse(POLY *s); void ossl_ml_dsa_poly_ntt_mult(const POLY *lhs, const POLY *rhs, POLY *out); @@ -75,9 +107,19 @@ uint32_t ossl_ml_dsa_key_compress_use_hint(uint32_t hint, uint32_t r, uint32_t gamma2); int ossl_ml_dsa_pk_encode(ML_DSA_KEY *key); -int ossl_ml_dsa_pk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key); +int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len); int ossl_ml_dsa_sk_encode(ML_DSA_KEY *key); -int ossl_ml_dsa_sk_decode(const uint8_t *in, size_t in_len, ML_DSA_KEY *key); +int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len); + +int ossl_ml_dsa_sig_encode(const ML_DSA_SIG *sig, const ML_DSA_PARAMS *params, + uint8_t *out); +int ossl_ml_dsa_sig_decode(ML_DSA_SIG *sig, const uint8_t *in, size_t in_len, + const ML_DSA_PARAMS *params); +int ossl_ml_dsa_w1_encode(const VECTOR *w1, uint32_t gamma2, + uint8_t *out, size_t out_len); +int ossl_ml_dsa_poly_decode_expand_mask(POLY *out, + const uint8_t *in, size_t in_len, + uint32_t gamma1); /* * @brief Reduces x mod q in constant time @@ -107,4 +149,35 @@ static ossl_inline ossl_unused uint32_t mod_sub(uint32_t a, uint32_t b) return reduce_once(ML_DSA_Q + a - b); } +/* + * @brief Returns the absolute value in constant time. + * i.e. return is_positive(x) ? x : -x; + * Note: MSVC doesn't like applying the unary minus operator to unsigned types + * (warning C4146), so we write the negation as a bitwise not plus one + * (assuming two's complement representation). + */ +static ossl_inline ossl_unused uint32_t abs_signed(uint32_t x) +{ + return constant_time_select_int(constant_time_lt(x, 0x80000000), x, 0u - x); +} + +/* + * @brief Returns the absolute value modulo q in constant time + * i.e return x > (q-1)/2 ? q - x : x; + */ +static ossl_inline ossl_unused uint32_t abs_mod_prime(uint32_t x) +{ + return constant_time_select_int(constant_time_lt(ML_DSA_Q_MINUS1_DIV2, x), + ML_DSA_Q - x, x); +} + +/* + * @brief Returns the maximum of two values in constant time. + * i.e return x < y ? y : x; + */ +static ossl_inline ossl_unused uint32_t maximum(uint32_t x, uint32_t y) +{ + return constant_time_select_int(constant_time_lt(x, y), y, x); +} + #endif /* OSSL_CRYPTO_ML_DSA_LOCAL_H */ diff --git a/crypto/ml_dsa/ml_dsa_matrix.h b/crypto/ml_dsa/ml_dsa_matrix.h index 2a79c6c59dd..afbe6e31df1 100644 --- a/crypto/ml_dsa/ml_dsa_matrix.h +++ b/crypto/ml_dsa/ml_dsa_matrix.h @@ -7,12 +7,21 @@ * https://www.openssl.org/source/license.html */ -/* A 'k' by 'l' Matrix object ('k' rows and 'l' columns) containing polynomial entries */ +/* A 'k' by 'l' Matrix object ('k' rows and 'l' columns) containing polynomial scalars */ struct matrix_st { POLY *m_poly; size_t k, l; }; +/** + * @brief Initialize a Matrix object. + * + * @param m The matrix object. + * @param polys A preallocated array of k * l polynomial blocks. |m| does not + * own/free this. + * @param k The number of rows + * @param l The number of columns + */ static ossl_inline ossl_unused void matrix_init(MATRIX *m, POLY *polys, size_t k, size_t l) { @@ -21,5 +30,14 @@ matrix_init(MATRIX *m, POLY *polys, size_t k, size_t l) m->m_poly = polys; } -void ossl_ml_dsa_matrix_mult_vector(const MATRIX *matrix_kl, const VECTOR *vl, - VECTOR *vk); +static ossl_inline ossl_unused void +matrix_mult_vector(const MATRIX *a, const VECTOR *s, VECTOR *t) +{ + ossl_ml_dsa_matrix_mult_vector(a, s, t); +} + +static ossl_inline ossl_unused int +matrix_expand_A(EVP_MD_CTX *g_ctx, const uint8_t *rho, MATRIX *out) +{ + return ossl_ml_dsa_matrix_expand_A(g_ctx, rho, out); +} diff --git a/crypto/ml_dsa/ml_dsa_params.c b/crypto/ml_dsa/ml_dsa_params.c index 63b50c6d2cf..e1a86943f99 100644 --- a/crypto/ml_dsa/ml_dsa_params.c +++ b/crypto/ml_dsa/ml_dsa_params.c @@ -11,14 +11,35 @@ #include "ml_dsa_local.h" #include "ml_dsa_params.h" -/* - * See FIPS 204 Section 4 Table 1 & Table 2 - * tau strength gamma1 k l eta beta omega sc sklen pklen siglen - */ -#define OSSL_ML_DSA_65 49, 192, 1 << 19, 6, 5, 4, 196, 55, 3, 4032, 1952, 3309 +/* See FIPS 204 Section 4 Table 1 & Table 2 */ +#define ML_DSA_65_TAU 49 +#define ML_DSA_65_LAMBDA 192 +#define ML_DSA_65_K 6 +#define ML_DSA_65_L 5 +#define ML_DSA_65_ETA ML_DSA_ETA_4 +#define ML_DSA_65_BETA 196 +#define ML_DSA_65_OMEGA 55 +#define ML_DSA_65_SECURITY_CATEGORY 3 +#define ML_DSA_65_PRIV_LEN 4032 +#define ML_DSA_65_PUB_LEN 1952 +#define ML_DSA_65_SIG_LEN 3309 static const ML_DSA_PARAMS ml_dsa_params[] = { - {"ML-DSA-65", OSSL_ML_DSA_65}, + { "ML-DSA-65", + ML_DSA_65_TAU, + ML_DSA_65_LAMBDA, + ML_DSA_GAMMA1_TWO_POWER_19, + ML_DSA_GAMMA2_Q_MINUS1_DIV32, + ML_DSA_65_K, + ML_DSA_65_L, + ML_DSA_65_ETA, + ML_DSA_65_BETA, + ML_DSA_65_OMEGA, + ML_DSA_65_SECURITY_CATEGORY, + ML_DSA_65_PRIV_LEN, + ML_DSA_65_PUB_LEN, + ML_DSA_65_SIG_LEN + }, {NULL}, }; diff --git a/crypto/ml_dsa/ml_dsa_params.h b/crypto/ml_dsa/ml_dsa_params.h index 1645735ef00..e6f4725aac9 100644 --- a/crypto/ml_dsa/ml_dsa_params.h +++ b/crypto/ml_dsa/ml_dsa_params.h @@ -16,8 +16,9 @@ struct ml_dsa_params_st { const char *alg; int tau; /* Number of +/-1's in polynomial c */ - int strength; /* The collision strength */ + int bit_strength; /* The collision strength (lambda) */ int gamma1; /* coefficient range of y */ + int gamma2; /* coefficient range of ? */ size_t k, l; /* matrix dimensions of 'A' */ int eta; /* Private key range */ int beta; /* tau * eta */ diff --git a/crypto/ml_dsa/ml_dsa_poly.h b/crypto/ml_dsa/ml_dsa_poly.h index 734bfb9b1e4..9c4696499ca 100644 --- a/crypto/ml_dsa/ml_dsa_poly.h +++ b/crypto/ml_dsa/ml_dsa_poly.h @@ -15,6 +15,12 @@ struct poly_st { uint32_t coeff[ML_DSA_NUM_POLY_COEFFICIENTS]; }; +static ossl_inline ossl_unused void +poly_zero(POLY *p) +{ + memset(p->coeff, 0, sizeof(*p)); +} + /** * @brief Polynomial addition. * @@ -58,6 +64,29 @@ poly_equal(const POLY *a, const POLY *b) return CRYPTO_memcmp(a, b, sizeof(*a)) == 0; } +static ossl_inline ossl_unused void +poly_ntt(POLY *p) +{ + ossl_ml_dsa_poly_ntt(p); +} + +static ossl_inline ossl_unused int +poly_sample_in_ball_ntt(POLY *out, const uint8_t *seed, int seed_len, + EVP_MD_CTX *h_ctx, uint32_t tau) +{ + if (!ossl_ml_dsa_poly_sample_in_ball(out, seed, seed_len, h_ctx, tau)) + return 0; + poly_ntt(out); + return 1; +} + +static ossl_inline ossl_unused int +poly_expand_mask(POLY *out, const uint8_t *seed, size_t seed_len, + uint32_t gamma1, EVP_MD_CTX *h_ctx) +{ + return ossl_ml_dsa_poly_expand_mask(out, seed, seed_len, gamma1, h_ctx); +} + /** * @brief Decompose the coefficients of a polynomial into (r1, r0) such that * coeff[i] == t1[i] * 2^13 + t0[i] mod q @@ -76,5 +105,107 @@ poly_power2_round(const POLY *t, POLY *t1, POLY *t0) for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) ossl_ml_dsa_key_compress_power2_round(t->coeff[i], - &t1->coeff[i], &t0->coeff[i]); + t1->coeff + i, t0->coeff + i); +} + +static ossl_inline ossl_unused void +poly_scale_power2_round(POLY *in, POLY *out) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) + out->coeff[i] = (in->coeff[i] << ML_DSA_D_BITS); +} + +static ossl_inline ossl_unused void +poly_high_bits(const POLY *in, uint32_t gamma2, POLY *out) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) + out->coeff[i] = ossl_ml_dsa_key_compress_high_bits(in->coeff[i], gamma2); +} + +static ossl_inline ossl_unused void +poly_low_bits(const POLY *in, uint32_t gamma2, POLY *out) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) + out->coeff[i] = ossl_ml_dsa_key_compress_low_bits(in->coeff[i], gamma2); +} + +static ossl_inline ossl_unused void +poly_make_hint(const POLY *ct0, const POLY *cs2, const POLY *w, uint32_t gamma2, + POLY *out) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) + out->coeff[i] = ossl_ml_dsa_key_compress_make_hint(ct0->coeff[i], + cs2->coeff[i], + gamma2, w->coeff[i]); +} + +static ossl_inline ossl_unused void +poly_use_hint(const POLY *h, const POLY *r, uint32_t gamma2, POLY *out) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) + out->coeff[i] = ossl_ml_dsa_key_compress_use_hint(h->coeff[i], + r->coeff[i], gamma2); +} + +static ossl_inline ossl_unused void +poly_max(const POLY *p, uint32_t *mx) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) { + uint32_t c = p->coeff[i]; + uint32_t abs = abs_mod_prime(c); + + *mx = maximum(*mx, abs); + } +} + +static ossl_inline ossl_unused void +poly_max_signed(const POLY *p, uint32_t *mx) +{ + int i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; i++) { + uint32_t c = p->coeff[i]; + uint32_t abs = abs_signed(c); + + *mx = maximum(*mx, abs); + } +} + +#if defined(ML_DSA_DEBUG) +static ossl_inline ossl_unused void poly_print(const POLY *p) +{ + size_t i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; ++i) { + if (i > 0 && ((i & 31) == 0)) + printf("\n"); + printf("%3x,", p->coeff[i]); + } + printf("\n"); +} + +static ossl_inline ossl_unused void poly_print_signed(const POLY *p) +{ + size_t i; + + for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS; ++i) { + if (i > 0 && ((i & 31) == 0)) + printf("\n"); + printf("%3d,", p->coeff[i] > ML_DSA_Q_MINUS1_DIV2 + ? (int)p->coeff[i] - (int)ML_DSA_Q : (int)p->coeff[i]); + } + printf("\n"); } +#endif diff --git a/crypto/ml_dsa/ml_dsa_sample.c b/crypto/ml_dsa/ml_dsa_sample.c index 52015c16b7e..0e7e5ef45ea 100644 --- a/crypto/ml_dsa/ml_dsa_sample.c +++ b/crypto/ml_dsa/ml_dsa_sample.c @@ -7,13 +7,15 @@ * https://www.openssl.org/source/license.html */ -#include #include "ml_dsa_local.h" #include "ml_dsa_vector.h" #include "ml_dsa_matrix.h" +#include "ml_dsa_hash.h" +#include "internal/sha3.h" +#include "internal/packet.h" -#define SHAKE128_BLOCKSIZE 168 -#define SHAKE256_BLOCKSIZE 136 +#define SHAKE128_BLOCKSIZE SHA3_BLOCKSIZE(128) +#define SHAKE256_BLOCKSIZE SHA3_BLOCKSIZE(256) typedef int (COEFF_FROM_NIBBLE_FUNC)(uint32_t nibble, uint32_t *out); @@ -104,23 +106,22 @@ static int rej_ntt_poly(EVP_MD_CTX *g_ctx, int j = 0; uint8_t blocks[SHAKE128_BLOCKSIZE], *b, *end = blocks + sizeof(blocks); - if (EVP_DigestInit_ex2(g_ctx, NULL, NULL) != 1 - || EVP_DigestUpdate(g_ctx, seed, seed_len) != 1) + /* + * Instead of just squeezing 3 bytes at a time, we grab a whole block + * Note that the shake128 blocksize of 168 is divisible by 3. + */ + if (!shake_xof(g_ctx, seed, seed_len, blocks, sizeof(blocks))) return 0; while (1) { - /* - * Instead of just squeezing 3 bytes at a time, we grab a whole block - * Note that the shake128 blocksize of 168 is divisible by 3. - */ - if (!EVP_DigestSqueeze(g_ctx, blocks, sizeof(blocks))) - return 0; for (b = blocks; b < end; b += 3) { if (coeff_from_three_bytes(b, &(out->coeff[j]))) { if (++j >= ML_DSA_NUM_POLY_COEFFICIENTS) return 1; /* finished */ } } + if (!EVP_DigestSqueeze(g_ctx, blocks, sizeof(blocks))) + return 0; } } @@ -148,14 +149,11 @@ static int rej_bounded_poly(EVP_MD_CTX *h_ctx, uint32_t z0, z1; uint8_t blocks[SHAKE256_BLOCKSIZE], *b, *end = blocks + sizeof(blocks); - if (EVP_DigestInit_ex2(h_ctx, NULL, NULL) != 1 - || EVP_DigestUpdate(h_ctx, seed, seed_len) != 1) + /* Instead of just squeezing 1 byte at a time, we grab a whole block */ + if (!shake_xof(h_ctx, seed, seed_len, blocks, sizeof(blocks))) return 0; while (1) { - /* Instead of just squeezing 1 byte at a time, we grab a whole block */ - if (!EVP_DigestSqueeze(h_ctx, blocks, sizeof(blocks))) - return 0; for (b = blocks; b < end; b++) { z0 = *b & 0x0F; /* lower nibble of byte */ z1 = *b >> 4; /* high nibble of byte */ @@ -167,6 +165,8 @@ static int rej_bounded_poly(EVP_MD_CTX *h_ctx, && ++j >= ML_DSA_NUM_POLY_COEFFICIENTS) return 1; } + if (!EVP_DigestSqueeze(h_ctx, blocks, sizeof(blocks))) + return 0; } } @@ -182,8 +182,8 @@ static int rej_bounded_poly(EVP_MD_CTX *h_ctx, * in the range of 0..q-1. * @returns 1 if the matrix was generated, or 0 on error. */ -int ossl_ml_dsa_sample_expandA(EVP_MD_CTX *g_ctx, const uint8_t *rho, - MATRIX *out) +int ossl_ml_dsa_matrix_expand_A(EVP_MD_CTX *g_ctx, const uint8_t *rho, + MATRIX *out) { int ret = 0; size_t i, j; @@ -224,8 +224,8 @@ err: * the range (q-eta)..0..eta * @returns 1 if s1 and s2 were successfully generated, or 0 otherwise. */ -int ossl_ml_dsa_sample_expandS(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, - VECTOR *s1, VECTOR *s2) +int ossl_ml_dsa_vector_expand_S(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, + VECTOR *s1, VECTOR *s2) { int ret = 0; size_t i; @@ -234,7 +234,7 @@ int ossl_ml_dsa_sample_expandS(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, uint8_t derived_seed[ML_DSA_PRIV_SEED_BYTES + 2]; COEFF_FROM_NIBBLE_FUNC *coef_from_nibble_fn; - coef_from_nibble_fn = (eta == 4) ? coeff_from_nibble_4 : coeff_from_nibble_2; + coef_from_nibble_fn = (eta == ML_DSA_ETA_4) ? coeff_from_nibble_4 : coeff_from_nibble_2; /* * Each polynomial generated uses a unique seed that consists of @@ -260,3 +260,82 @@ int ossl_ml_dsa_sample_expandS(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, err: return ret; } + +/* See FIPS 204, Algorithm 34, ExpandMask(), Step 4 & 5 */ +int ossl_ml_dsa_poly_expand_mask(POLY *out, + const uint8_t *seed, size_t seed_len, + uint32_t gamma1, EVP_MD_CTX *h_ctx) +{ + uint8_t buf[32 * 20]; + size_t buf_len = 32 * (gamma1 == ML_DSA_GAMMA1_TWO_POWER_19 ? 20 : 18); + + return shake_xof(h_ctx, seed, seed_len, buf, buf_len) + && ossl_ml_dsa_poly_decode_expand_mask(out, buf, buf_len, gamma1); +} + +/* + * @brief Sample a polynomial with coefficients in the range {-1..1}. + * The number of non zero values (hamming weight) is given by tau + * + * See FIPS 204, Algorithm 29, SampleInBall() + * This function is assumed to not be constant time. + * The algorithm is based on Durstenfeld's version of the Fisher-Yates shuffle. + * + * Note that the coefficients returned by this implementation are positive + * i.e one of q-1, 0, or 1. + * + * @param tau is the number of +1 or -1's in the polynomial 'out_c' (39, 49 or 60) + * that is less than or equal to 64 + */ +int ossl_ml_dsa_poly_sample_in_ball(POLY *out_c, const uint8_t *seed, int seed_len, + EVP_MD_CTX *h_ctx, uint32_t tau) +{ + uint8_t block[SHAKE256_BLOCKSIZE]; + uint64_t signs; + int offset = 8; + size_t end; + + /* + * Rather than squeeze 8 bytes followed by lots of 1 byte squeezes + * the SHAKE blocksize is squeezed each time and buffered into 'block'. + */ + if (!shake_xof(h_ctx, seed, seed_len, block, sizeof(block))) + return 0; + + /* + * grab the first 64 bits - since tau < 64 + * Each bit gives a +1 or -1 value. + */ + memcpy(&signs, block, 8); + + poly_zero(out_c); + + /* Loop tau times */ + for (end = 256 - tau; end < 256; end++) { + size_t index; /* index is a random offset to write +1 or -1 */ + + /* rejection sample in {0..end} to choose an index to place -1 or 1 into */ + for (;;) { + if (offset == sizeof(block)) { + /* squeeze another block if the bytes from block have been used */ + if (!EVP_DigestSqueeze(h_ctx, block, sizeof(block))) + return 0; + offset = 0; + } + + index = block[offset++]; + if (index <= end) + break; + } + + /* + * In-place swap the coefficient we are about to replace to the end so + * we don't lose any values that have been already written. + */ + out_c->coeff[end] = out_c->coeff[index]; + /* set the random coefficient value to either 1 or q-1 */ + out_c->coeff[index] = mod_sub(1, 2 * (signs & 1)); + signs >>= 1; /* grab the next random bit */ + } + return 1; +} diff --git a/crypto/ml_dsa/ml_dsa_sign.c b/crypto/ml_dsa/ml_dsa_sign.c index adb9435a318..f7abc1bad01 100644 --- a/crypto/ml_dsa/ml_dsa_sign.c +++ b/crypto/ml_dsa/ml_dsa_sign.c @@ -15,150 +15,380 @@ #include "ml_dsa_key.h" #include "ml_dsa_params.h" #include "ml_dsa_matrix.h" +#include "ml_dsa_sign.h" +#include "ml_dsa_hash.h" +#define ML_DSA_MAX_LAMBDA 256 /* bit strength for ML-DSA-87 */ + +/* + * @brief Initialize a Signature object by pointing all of its objects to + * preallocated blocks. The values passed for hint, z and + * c_tilde values are not owned/freed by the |sig| object. + * + * @param sig The ML_DSA_SIG to initialize. + * @param hint A preallocated array of |k| polynomial blocks + * @param k The number of |hint| polynomials + * @param z A preallocated array of |l| polynomial blocks + * @param l The number of |z| polynomials + * @param c_tilde A preallocated buffer + * @param c_tilde_len The size of |c_tilde| + */ +static void signature_init(ML_DSA_SIG *sig, + POLY *hint, uint32_t k, POLY *z, uint32_t l, + uint8_t *c_tilde, size_t c_tilde_len) +{ + vector_init(&sig->z, z, l); + vector_init(&sig->hint, hint, k); + sig->c_tilde = c_tilde; + sig->c_tilde_len = c_tilde_len; +} /* * FIPS 204, Algorithm 7, ML-DSA.Sign_internal() * @returns 1 on success and 0 on failure. */ -template -static int ossl_ml_dsa_sign_internal( - uint8_t out_encoded_signature[signature_bytes()], - const struct private_key *priv, const uint8_t *msg, size_t msg_len, - const uint8_t *context_prefix, size_t context_prefix_len, - const uint8_t *context, size_t context_len, - const uint8_t randomizer[MLDSA_SIGNATURE_RANDOMIZER_BYTES]) { - uint8_t mu[kMuBytes]; - struct BORINGSSL_keccak_st keccak_ctx; - BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake256); - BORINGSSL_keccak_absorb(&keccak_ctx, priv->public_key_hash, - sizeof(priv->public_key_hash)); - BORINGSSL_keccak_absorb(&keccak_ctx, context_prefix, context_prefix_len); - BORINGSSL_keccak_absorb(&keccak_ctx, context, context_len); - BORINGSSL_keccak_absorb(&keccak_ctx, msg, msg_len); - BORINGSSL_keccak_squeeze(&keccak_ctx, mu, kMuBytes); - - uint8_t rho_prime[kRhoPrimeBytes]; - BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake256); - BORINGSSL_keccak_absorb(&keccak_ctx, priv->k, sizeof(priv->k)); - BORINGSSL_keccak_absorb(&keccak_ctx, randomizer, - MLDSA_SIGNATURE_RANDOMIZER_BYTES); - BORINGSSL_keccak_absorb(&keccak_ctx, mu, kMuBytes); - BORINGSSL_keccak_squeeze(&keccak_ctx, rho_prime, kRhoPrimeBytes); - - // Intermediate values, allocated on the heap to allow use when there is a - // limited amount of stack. - struct values_st { - struct signature sign; - vector s1_ntt; - vector s2_ntt; - vector t0_ntt; - matrix a_ntt; - vector y; - vector w; - vector w1; - vector cs1; - vector cs2; - }; - std::unique_ptr> values( - reinterpret_cast(OPENSSL_malloc(sizeof(values_st)))); - if (values == NULL) { - return 0; - } - OPENSSL_memcpy(&values->s1_ntt, &priv->s1, sizeof(values->s1_ntt)); - vector_ntt(&values->s1_ntt); - - OPENSSL_memcpy(&values->s2_ntt, &priv->s2, sizeof(values->s2_ntt)); - vector_ntt(&values->s2_ntt); - - OPENSSL_memcpy(&values->t0_ntt, &priv->t0, sizeof(values->t0_ntt)); - vector_ntt(&values->t0_ntt); - - matrix_expand(&values->a_ntt, priv->rho); - - // kappa must not exceed 2**16/L = 13107. But the probability of it - // exceeding even 1000 iterations is vanishingly small. - for (size_t kappa = 0;; kappa += L) { - vector_expand_mask(&values->y, rho_prime, kappa); - - vector *y_ntt = &values->cs1; - OPENSSL_memcpy(y_ntt, &values->y, sizeof(*y_ntt)); - vector_ntt(y_ntt); - - matrix_mult(&values->w, &values->a_ntt, y_ntt); - vector_inverse_ntt(&values->w); - - vector_high_bits(&values->w1, &values->w); - uint8_t w1_encoded[128 * K]; - w1_encode(w1_encoded, &values->w1); - - BORINGSSL_keccak_init(&keccak_ctx, boringssl_shake256); - BORINGSSL_keccak_absorb(&keccak_ctx, mu, kMuBytes); - BORINGSSL_keccak_absorb(&keccak_ctx, w1_encoded, 128 * K); - BORINGSSL_keccak_squeeze(&keccak_ctx, values->sign.c_tilde, - 2 * lambda_bytes()); - - scalar c_ntt; - scalar_sample_in_ball_vartime(&c_ntt, values->sign.c_tilde, - sizeof(values->sign.c_tilde), tau()); - scalar_ntt(&c_ntt); - - vector_mult_scalar(&values->cs1, &values->s1_ntt, &c_ntt); - vector_inverse_ntt(&values->cs1); - vector_mult_scalar(&values->cs2, &values->s2_ntt, &c_ntt); - vector_inverse_ntt(&values->cs2); - - vector_add(&values->sign.z, &values->y, &values->cs1); - - vector *r0 = &values->w1; - vector_sub(r0, &values->w, &values->cs2); - vector_low_bits(r0, r0); - - // Leaking the fact that a signature was rejected is fine as the next - // attempt at a signature will be (indistinguishable from) independent of - // this one. Note, however, that we additionally leak which of the two - // branches rejected the signature. Section 5.5 of - // https://pq-crystals.org/dilithium/data/dilithium-specification-round3.pdf - // describes this leak as OK. Note we leak less than what is described by - // the paper; we do not reveal which coefficient violated the bound, and - // we hide which of the |z_max| or |r0_max| bound failed. See also - // https://boringssl-review.googlesource.com/c/boringssl/+/67747/comment/2bbab0fa_d241d35a/ - uint32_t z_max = vector_max(&values->sign.z); - uint32_t r0_max = vector_max_signed(r0); - if (constant_time_declassify_w( - constant_time_ge_w(z_max, gamma1() - beta()) | - constant_time_ge_w(r0_max, kGamma2 - beta()))) { - continue; - } +static int ml_dsa_sign_internal(ML_DSA_CTX *ctx, const ML_DSA_KEY *priv, + const uint8_t *encoded_msg, + size_t encoded_msg_len, + const uint8_t *rnd, size_t rnd_len, + uint8_t *out_sig) +{ + int ret = 0; + const ML_DSA_PARAMS *params = ctx->params; + EVP_MD_CTX *h_ctx = ctx->h_ctx; + uint32_t k = params->k, l = params->l; + uint32_t gamma1 = params->gamma1, gamma2 = params->gamma2; + uint8_t *alloc = NULL, *w1_encoded; + size_t w1_encoded_len = 128 * k; + size_t num_polys_sig_k = 2 * k; + size_t num_polys_k = 5 * k; + size_t num_polys_l = 3 * l; + size_t num_polys_k_by_l = k * l; + POLY *polys = NULL, *p, *c_ntt; + size_t alloc_len = w1_encoded_len + + sizeof(*polys) + * (1 + num_polys_k + num_polys_l + + num_polys_k_by_l + num_polys_sig_k); + VECTOR s1_ntt, s2_ntt, t0_ntt, w, w1, cs1, cs2, y; + MATRIX a_ntt; + ML_DSA_SIG sig; + uint8_t mu[ML_DSA_MU_BYTES]; + uint8_t rho_prime[ML_DSA_RHO_PRIME_BYTES]; + uint8_t c_tilde[ML_DSA_MAX_LAMBDA / 4]; + size_t c_tilde_len = params->bit_strength >> 2; + size_t kappa; + + /* + * Allocate a single blob for most of the variable size temporary variables. + * Mostly used for VECTOR POLYNOMIALS (every POLY is 1K). + */ + alloc = OPENSSL_malloc(alloc_len); + if (alloc == NULL) + return 0; + w1_encoded = alloc; + /* Init the temp vectors to point to the allocated polys blob */ + p = (POLY *)(w1_encoded + w1_encoded_len); + c_ntt = p++; + matrix_init(&a_ntt, p, k, l); + p += num_polys_k_by_l; + vector_init(&s2_ntt, p, k); + vector_init(&t0_ntt, s2_ntt.poly + k, k); + vector_init(&w, t0_ntt.poly + k, k); + vector_init(&w1, w.poly + k, k); + vector_init(&cs2, w1.poly + k, k); + p += num_polys_k; + vector_init(&s1_ntt, p, l); + vector_init(&y, p + l, l); + vector_init(&cs1, p + 2 * l, l); + p += num_polys_l; + signature_init(&sig, p, k, p + k, l, c_tilde, c_tilde_len); + /* End of the allocated blob setup */ + + if (!matrix_expand_A(ctx->g_ctx, priv->rho, &a_ntt) + || !shake_xof_2(h_ctx, priv->tr, sizeof(priv->tr), + encoded_msg, encoded_msg_len, mu, sizeof(mu)) + || !shake_xof_3(h_ctx, priv->K, sizeof(priv->K), rnd, rnd_len, + mu, sizeof(mu), rho_prime, sizeof(rho_prime))) + goto err; + + vector_copy(&s1_ntt, &priv->s1); + vector_ntt(&s1_ntt); + vector_copy(&s2_ntt, &priv->s2); + vector_ntt(&s2_ntt); + vector_copy(&t0_ntt, &priv->t0); + vector_ntt(&t0_ntt); + + /* + * kappa must not exceed 2^16. But the probability of it + * exceeding even 1000 iterations is vanishingly small. + */ + for (kappa = 0; ; kappa += l) { + VECTOR *y_ntt = &cs1; + VECTOR *r0 = &w1; + VECTOR *ct0 = &w1; + uint32_t z_max, r0_max, ct0_max, h_ones; + + vector_expand_mask(&y, rho_prime, sizeof(rho_prime), kappa, + gamma1, ctx->h_ctx); + vector_copy(y_ntt, &y); + vector_ntt(y_ntt); + + matrix_mult_vector(&a_ntt, y_ntt, &w); + vector_ntt_inverse(&w); - vector *ct0 = &values->w1; - vector_mult_scalar(ct0, &values->t0_ntt, &c_ntt); - vector_inverse_ntt(ct0); - vector_make_hint(&values->sign.h, ct0, &values->cs2, &values->w); - - // See above. - uint32_t ct0_max = vector_max(ct0); - size_t h_ones = vector_count_ones(&values->sign.h); - if (constant_time_declassify_w(constant_time_ge_w(ct0_max, kGamma2) | - constant_time_lt_w(omega(), h_ones))) { - continue; + vector_high_bits(&w, gamma2, &w1); + ossl_ml_dsa_w1_encode(&w1, gamma2, w1_encoded, w1_encoded_len); + + if (!shake_xof_2(h_ctx, mu, sizeof(mu), w1_encoded, 128 * k, + c_tilde, c_tilde_len)) + break; + + if (!poly_sample_in_ball_ntt(c_ntt, c_tilde, c_tilde_len, ctx->h_ctx, + params->tau)) + break; + + vector_mult_scalar(&s1_ntt, c_ntt, &cs1); + vector_ntt_inverse(&cs1); + vector_mult_scalar(&s2_ntt, c_ntt, &cs2); + vector_ntt_inverse(&cs2); + + vector_add(&y, &cs1, &sig.z); + + /* r0 = lowbits(w - cs2) */ + vector_sub(&w, &cs2, r0); + vector_low_bits(r0, gamma2, r0); + + /* + * Leaking that the signature is rejected is fine as the next attempt at a + * signature will be (indistinguishable from) independent of this one. + */ + z_max = vector_max(&sig.z); + r0_max = vector_max_signed(r0); + if (value_barrier_32(constant_time_ge(z_max, gamma1 - params->beta) + | constant_time_ge(r0_max, gamma2 - params->beta))) + continue; + + vector_mult_scalar(&t0_ntt, c_ntt, ct0); + vector_ntt_inverse(ct0); + vector_make_hint(ct0, &cs2, &w, gamma2, &sig.hint); + + ct0_max = vector_max(ct0); + h_ones = vector_count_ones(&sig.hint); + /* Same reasoning applies to the leak as above */ + if (value_barrier_32(constant_time_ge(ct0_max, gamma2) + | constant_time_lt(params->omega, h_ones))) + continue; + ret = ossl_ml_dsa_sig_encode(&sig, params, out_sig); + break; } +err: + OPENSSL_clear_free(alloc, alloc_len); + OPENSSL_cleanse(rho_prime, sizeof(rho_prime)); + return ret; +} + +/* + * See FIPS 204, Algorithm 8, ML-DSA.Verify_internal(). + */ +static int ml_dsa_verify_internal(ML_DSA_CTX *ctx, const ML_DSA_KEY *pub, + const uint8_t *msg_enc, size_t msg_enc_len, + const uint8_t *sig_enc, size_t sig_enc_len) +{ + int ret = 0; + uint8_t *alloc = NULL, *w1_encoded; + POLY *polys = NULL, *p, *c_ntt; + MATRIX a_ntt; + VECTOR az_ntt, ct1_ntt, *z_ntt, *w1, *w_approx; + ML_DSA_SIG sig; + const ML_DSA_PARAMS *params = ctx->params; + uint32_t k = pub->params->k; + uint32_t l = pub->params->l; + size_t w1_encoded_len = 128 * k; + size_t num_polys_sig = k + l; + size_t num_polys_k = 2 * k; + size_t num_polys_l = 1 * l; + size_t num_polys_k_by_l = k * l; + uint8_t mu[ML_DSA_MU_BYTES]; + uint8_t c_tilde[ML_DSA_MAX_LAMBDA / 4]; + uint8_t c_tilde_sig[ML_DSA_MAX_LAMBDA / 4]; + EVP_MD_CTX *h_ctx = ctx->h_ctx; + size_t c_tilde_len = params->bit_strength >> 2; + uint32_t z_max; - // Although computed with the private key, the signature is public. - CONSTTIME_DECLASSIFY(values->sign.c_tilde, sizeof(values->sign.c_tilde)); - CONSTTIME_DECLASSIFY(&values->sign.z, sizeof(values->sign.z)); - CONSTTIME_DECLASSIFY(&values->sign.h, sizeof(values->sign.h)); + /* Allocate space for all the POLYNOMIALS used by temporary VECTORS */ + alloc = OPENSSL_malloc(w1_encoded_len + + sizeof(*polys) * (1 + num_polys_k + + num_polys_l + + num_polys_k_by_l + + num_polys_sig)); + if (alloc == NULL) + return 0; + w1_encoded = alloc; + /* Init the temp vectors to point to the allocated polys blob */ + p = (POLY *)(w1_encoded + w1_encoded_len); + c_ntt = p++; + matrix_init(&a_ntt, p, k, l); + p += num_polys_k_by_l; + signature_init(&sig, p, k, p + k, l, c_tilde_sig, c_tilde_len); + p += num_polys_sig; + vector_init(&az_ntt, p, k); + vector_init(&ct1_ntt, p + k, k); - CBB cbb; - CBB_init_fixed(&cbb, out_encoded_signature, signature_bytes()); - if (!mldsa_marshal_signature(&cbb, &values->sign)) { - return 0; + if (!ossl_ml_dsa_sig_decode(&sig, sig_enc, sig_enc_len, ctx->params) + || !matrix_expand_A(ctx->g_ctx, pub->rho, &a_ntt) + || !shake_xof_2(h_ctx, pub->tr, sizeof(pub->tr), + msg_enc, msg_enc_len, mu, sizeof(mu))) + goto err; + /* Compute verifiers challenge c_ntt = NTT(SampleInBall(c_tilde) */ + if (!poly_sample_in_ball_ntt(c_ntt, c_tilde_sig, c_tilde_len, ctx->h_ctx, + params->tau)) + goto err; + + /* ct1_ntt = NTT(c) * NTT(t1 * 2^d) */ + vector_scale_power2_round_ntt(&pub->t1, &ct1_ntt); + vector_mult_scalar(&ct1_ntt, c_ntt, &ct1_ntt); + + /* compute z_max early in order to reuse sig.z */ + z_max = vector_max(&sig.z); + + /* w_approx = NTT_inverse(A * NTT(z) - ct1_ntt) */ + z_ntt = &sig.z; + vector_ntt(z_ntt); + matrix_mult_vector(&a_ntt, z_ntt, &az_ntt); + w_approx = &az_ntt; + vector_sub(&az_ntt, &ct1_ntt, w_approx); + vector_ntt_inverse(w_approx); + + /* compute w1_encoded */ + w1 = w_approx; + vector_use_hint(&sig.hint, w_approx, params->gamma2, w1); + ossl_ml_dsa_w1_encode(w1, params->gamma2, w1_encoded, w1_encoded_len); + + if (!shake_xof_3(h_ctx, mu, sizeof(mu), w1_encoded, w1_encoded_len, NULL, 0, + c_tilde, c_tilde_len)) + goto err; + + ret = (z_max < (uint32_t)(params->gamma1 - params->beta)) + && memcmp(c_tilde, sig.c_tilde, c_tilde_len) == 0; +err: + OPENSSL_free(alloc); + return ret; +} + +/** + * @brief Encode a message + * See FIPS 204 Algorithm 2 Step 10 (and algorithm 3 Step 5). + * + * ML_DSA pure signatures are encoded as M' = 00 || ctx_len || ctx || msg + * Where ctx is the empty string by default and ctx_len <= 255. + * + * Note this code could be shared with SLH_DSA + * + * @param msg A message to encode + * @param msg_len The size of |msg| + * @param ctx An optional context to add to the message encoding. + * @param ctx_len The size of |ctx|. It must be in the range 0..255 + * @param encode Use the Pure signature encoding if this is 1, and dont encode + * if this value is 0. + * @param tmp A small buffer that may be used if the message is small. + * @param tmp_len The size of |tmp| + * @param out_len The size of the returned encoded buffer. + * @returns A buffer containing the encoded message. If the passed in + * |tmp| buffer is big enough to hold the encoded message then it returns |tmp| + * otherwise it allocates memory which must be freed by the caller. If |encode| + * is 0 then it returns |msg|. NULL is returned if there is a failure. + */ +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 > ML_DSA_MAX_CONTEXT_STRING_LEN) + return NULL; - BSSL_CHECK(CBB_len(&cbb) == signature_bytes()); - return 1; - } + /* 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; } +/** + * See FIPS 204 Section 5.2 Algorithm 2 ML-DSA.Sign() + * + * @returns 1 on success, or 0 on error. + */ +int ossl_ml_dsa_sign(ML_DSA_CTX *ctx, const ML_DSA_KEY *priv, + const uint8_t *msg, size_t msg_len, + const uint8_t *context, size_t context_len, + const uint8_t *rand, size_t rand_len, int encode, + unsigned char *sig, size_t *sig_len, size_t sig_size) +{ + int ret = 1; + uint8_t m_tmp[1024], *m = m_tmp; + size_t m_len = 0; + if (ossl_ml_dsa_key_get_priv(priv) == NULL) + return 0; + if (sig != NULL) { + if (sig_size < ctx->params->sig_len) + return 0; + m = msg_encode(msg, msg_len, context, context_len, encode, + m_tmp, sizeof(m_tmp), &m_len); + if (m == NULL) + return 0; + ret = ml_dsa_sign_internal(ctx, priv, m, m_len, rand, rand_len, + sig); + if (m != msg && m != m_tmp) + OPENSSL_free(m); + } + if (sig_len != NULL) + *sig_len = ctx->params->sig_len; + return ret; +} +/** + * See FIPS 203 Section 5.3 Algorithm 3 ML-DSA.Verify() + * @returns 1 on success, or 0 on error. + */ +int ossl_ml_dsa_verify(ML_DSA_CTX *ctx, ML_DSA_KEY *pub, + const uint8_t *msg, size_t msg_len, + const uint8_t *context, size_t context_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; + + if (ossl_ml_dsa_key_get_pub(pub) == NULL + && !ossl_ml_dsa_key_public_from_private(pub, ctx)) + return 0; + + m = msg_encode(msg, msg_len, context, context_len, encode, + m_tmp, sizeof(m_tmp), &m_len); + if (m == NULL) + return 0; + + ret = ml_dsa_verify_internal(ctx, pub, m, m_len, sig, sig_len); + if (m != msg && m != m_tmp) + OPENSSL_free(m); + return ret; +} diff --git a/crypto/ml_dsa/ml_dsa_sign.h b/crypto/ml_dsa/ml_dsa_sign.h new file mode 100644 index 00000000000..6167da6042d --- /dev/null +++ b/crypto/ml_dsa/ml_dsa_sign.h @@ -0,0 +1,15 @@ +/* + * 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 + */ + +struct ml_dsa_sig_st { + VECTOR z; + VECTOR hint; + uint8_t *c_tilde; + size_t c_tilde_len; +}; diff --git a/crypto/ml_dsa/ml_dsa_vector.h b/crypto/ml_dsa/ml_dsa_vector.h index 5aa84511c2e..64f4efde048 100644 --- a/crypto/ml_dsa/ml_dsa_vector.h +++ b/crypto/ml_dsa/ml_dsa_vector.h @@ -14,7 +14,14 @@ struct vector_st { size_t num_poly; }; -/* @brief Set the number of polynomial elements that will be present in the vector */ +/** + * @brief Initialize a Vector object. + * + * @param v The vector to initialize. + * @param polys Preallocated storage for an array of Polynomials blocks. |v| + * does not own/free this. + * @param num_polys The number of |polys| blocks (k or l) + */ static ossl_inline ossl_unused void vector_init(VECTOR *v, POLY *polys, size_t num_polys) { @@ -29,6 +36,29 @@ void vector_zero(VECTOR *va) memset(va->poly, 0, va->num_poly * sizeof(va->poly[0])); } +/* @brief copy a vector */ +static ossl_inline ossl_unused void +vector_copy(VECTOR *dst, const VECTOR *src) +{ + dst->num_poly = src->num_poly; + memcpy(dst->poly, src->poly, src->num_poly * sizeof(src->poly[0])); +} + +/* @brief return 1 if 2 vectors are equal, or 0 otherwise */ +static ossl_inline ossl_unused int +vector_equal(const VECTOR *a, const VECTOR *b) +{ + size_t i; + + if (a->num_poly != b->num_poly) + return 0; + for (i = 0; i < a->num_poly; ++i) { + if (!poly_equal(a->poly + i, b->poly + i)) + return 0; + } + return 1; +} + /* @brief add 2 vectors */ static ossl_inline ossl_unused void vector_add(const VECTOR *lhs, const VECTOR *rhs, VECTOR *out) @@ -36,7 +66,7 @@ vector_add(const VECTOR *lhs, const VECTOR *rhs, VECTOR *out) size_t i; for (i = 0; i < lhs->num_poly; i++) - poly_add(&lhs->poly[i], &rhs->poly[i], &out->poly[i]); + poly_add(lhs->poly + i, rhs->poly + i, out->poly + i); } /* @brief subtract 2 vectors */ @@ -46,60 +76,74 @@ vector_sub(const VECTOR *lhs, const VECTOR *rhs, VECTOR *out) size_t i; for (i = 0; i < lhs->num_poly; i++) - poly_sub(&lhs->poly[i], &rhs->poly[i], &out->poly[i]); + poly_sub(lhs->poly + i, rhs->poly + i, out->poly + i); } -/* @brief multiply a vector by a polynomial */ +/* @brief convert a vector in place into NTT form */ static ossl_inline ossl_unused void -vector_ntt_mult_poly(const VECTOR *lhs, const POLY *rhs, VECTOR *out) +vector_ntt(VECTOR *va) { size_t i; - for (i = 0; i < lhs->num_poly; i++) - ossl_ml_dsa_poly_ntt_mult(&lhs->poly[i], rhs, &out->poly[i]); + for (i = 0; i < va->num_poly; i++) + ossl_ml_dsa_poly_ntt(va->poly + i); } -/* @brief copy a vector */ +/* @brief convert a vector in place into inverse NTT form */ static ossl_inline ossl_unused void -vector_copy(VECTOR *dst, const VECTOR *src) +vector_ntt_inverse(VECTOR *va) { - dst->num_poly = src->num_poly; - memcpy(dst->poly, src->poly, src->num_poly * sizeof(src->poly[0])); + size_t i; + + for (i = 0; i < va->num_poly; i++) + ossl_ml_dsa_poly_ntt_inverse(va->poly + i); } -/* @brief return 1 if 2 vectors are equal, or 0 otherwise */ -static ossl_inline ossl_unused int -vector_equal(const VECTOR *a, const VECTOR *b) +/* @brief multiply a vector by a SCALAR polynomial */ +static ossl_inline ossl_unused void +vector_mult_scalar(const VECTOR *lhs, const POLY *rhs, VECTOR *out) { size_t i; - if (a->num_poly != b->num_poly) - return 0; - for (i = 0; i < a->num_poly; ++i) { - if (!poly_equal(a->poly + i, b->poly + i)) - return 0; - } - return 1; + for (i = 0; i < lhs->num_poly; i++) + ossl_ml_dsa_poly_ntt_mult(lhs->poly + i, rhs, out->poly + i); +} + +static ossl_inline ossl_unused int +vector_expand_S(EVP_MD_CTX *h_ctx, int eta, const uint8_t *seed, + VECTOR *s1, VECTOR *s2) +{ + return ossl_ml_dsa_vector_expand_S(h_ctx, eta, seed, s1, s2); } -/* @brief convert a vector in place into NTT form */ static ossl_inline ossl_unused void -vector_ntt(VECTOR *va) +vector_expand_mask(VECTOR *out, const uint8_t *rho_prime, size_t rho_prime_len, + uint32_t kappa, uint32_t gamma1, EVP_MD_CTX *h_ctx) { size_t i; + uint8_t derived_seed[ML_DSA_RHO_PRIME_BYTES + 2]; - for (i = 0; i < va->num_poly; i++) - ossl_ml_dsa_poly_ntt(&va->poly[i]); + memcpy(derived_seed, rho_prime, ML_DSA_RHO_PRIME_BYTES); + + for (i = 0; i < out->num_poly; i++) { + size_t index = kappa + i; + + derived_seed[ML_DSA_RHO_PRIME_BYTES] = index & 0xFF; + derived_seed[ML_DSA_RHO_PRIME_BYTES + 1] = (index >> 8) & 0xFF; + poly_expand_mask(out->poly + i, derived_seed, sizeof(derived_seed), + gamma1, h_ctx); + } } -/* @brief convert a vector in place into inverse NTT form */ +/* Scale back previously rounded value */ static ossl_inline ossl_unused void -vector_ntt_inverse(VECTOR *va) +vector_scale_power2_round_ntt(const VECTOR *in, VECTOR *out) { size_t i; - for (i = 0; i < va->num_poly; i++) - ossl_ml_dsa_poly_ntt_inverse(&va->poly[i]); + for (i = 0; i < in->num_poly; i++) + poly_scale_power2_round(in->poly + i, out->poly + i); + vector_ntt(out); } /* @@ -113,5 +157,101 @@ vector_power2_round(const VECTOR *t, VECTOR *t1, VECTOR *t0) size_t i; for (i = 0; i < t->num_poly; i++) - poly_power2_round(&t->poly[i], &t1->poly[i], &t0->poly[i]); + poly_power2_round(t->poly + i, t1->poly + i, t0->poly + i); +} + +static ossl_inline ossl_unused void +vector_high_bits(const VECTOR *in, uint32_t gamma2, VECTOR *out) +{ + size_t i; + + for (i = 0; i < out->num_poly; i++) + poly_high_bits(in->poly + i, gamma2, out->poly + i); +} + +static ossl_inline ossl_unused void +vector_low_bits(const VECTOR *in, uint32_t gamma2, VECTOR *out) +{ + size_t i; + + for (i = 0; i < out->num_poly; i++) + poly_low_bits(in->poly + i, gamma2, out->poly + i); +} + +static ossl_inline ossl_unused uint32_t +vector_max(const VECTOR *v) +{ + size_t i; + uint32_t mx = 0; + + for (i = 0; i < v->num_poly; i++) + poly_max(v->poly + i, &mx); + return mx; +} + +static ossl_inline ossl_unused uint32_t +vector_max_signed(const VECTOR *v) +{ + size_t i; + uint32_t mx = 0; + + for (i = 0; i < v->num_poly; i++) + poly_max_signed(v->poly + i, &mx); + return mx; +} + +static ossl_inline ossl_unused size_t +vector_count_ones(const VECTOR *v) +{ + int j; + size_t i, count = 0; + + for (i = 0; i < v->num_poly; i++) + for (j = 0; j < ML_DSA_NUM_POLY_COEFFICIENTS; j++) + count += v->poly[i].coeff[j]; + return count; +} + +static ossl_inline ossl_unused void +vector_make_hint(const VECTOR *ct0, const VECTOR *cs2, const VECTOR *w, + uint32_t gamma2, VECTOR *out) +{ + size_t i; + + for (i = 0; i < out->num_poly; i++) + poly_make_hint(ct0->poly + i, cs2->poly + i, w->poly + i, gamma2, + out->poly + i); +} + +static ossl_inline ossl_unused void +vector_use_hint(const VECTOR *h, const VECTOR *r, uint32_t gamma2, VECTOR *out) +{ + size_t i; + + for (i = 0; i < out->num_poly; i++) + poly_use_hint(h->poly + i, r->poly + i, gamma2, out->poly + i); +} + +#if defined(ML_DSA_DEBUG) +static ossl_inline ossl_unused void +vector_print(const char * name, const VECTOR *v) +{ + size_t i; + + printf("\nVECTOR %s:\n", name); + + for (i = 0; i < v->num_poly; ++i) + poly_print(v->poly + i); +} + +static ossl_inline ossl_unused void +vector_print_signed(const char * name, const VECTOR *v) +{ + size_t i; + + printf("\nVECTOR %s:\n", name); + + for (i = 0; i < v->num_poly; ++i) + poly_print_signed(v->poly + i); } +#endif diff --git a/include/crypto/ml_dsa.h b/include/crypto/ml_dsa.h index 124e65441d0..e93539670ed 100644 --- a/include/crypto/ml_dsa.h +++ b/include/crypto/ml_dsa.h @@ -28,6 +28,7 @@ __owur int ossl_ml_dsa_key_equal(const ML_DSA_KEY *key1, const ML_DSA_KEY *key2, int selection); __owur int ossl_ml_dsa_key_has(const ML_DSA_KEY *key, int selection); __owur int ossl_ml_dsa_key_pairwise_check(const ML_DSA_KEY *key); +__owur int ossl_ml_dsa_key_public_from_private(ML_DSA_KEY *key, ML_DSA_CTX *ctx); __owur int ossl_ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM *params, int include_private); __owur int ossl_ml_dsa_generate_key(ML_DSA_CTX *ctx, OSSL_LIB_CTX *libctx, @@ -52,4 +53,14 @@ __owur ML_DSA_CTX *ossl_ml_dsa_ctx_new(const char *alg, OSSL_LIB_CTX *lib_ctx, const char *propq); void ossl_ml_dsa_ctx_free(ML_DSA_CTX *ctx); +__owur int ossl_ml_dsa_sign(ML_DSA_CTX *ctx, const ML_DSA_KEY *priv, + const uint8_t *msg, size_t msg_len, + const uint8_t *context, size_t context_len, + const uint8_t *rand, size_t rand_len, int encode, + unsigned char *sig, size_t *siglen, size_t sigsize); +__owur int ossl_ml_dsa_verify(ML_DSA_CTX *ctx, ML_DSA_KEY *pub, + const uint8_t *msg, size_t msg_len, + const uint8_t *context, size_t context_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 48618641306..2b8b8c5c00e 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -446,6 +446,9 @@ static const OSSL_ALGORITHM deflt_signature[] = { # ifndef OPENSSL_NO_SM2 { PROV_NAMES_SM2, "provider=default", ossl_sm2_signature_functions }, # endif +#endif +#ifndef OPENSSL_NO_ML_DSA + { PROV_NAMES_ML_DSA_65, "provider=default", ossl_ml_dsa_65_signature_functions }, #endif { PROV_NAMES_HMAC, "provider=default", ossl_mac_legacy_hmac_signature_functions }, { PROV_NAMES_SIPHASH, "provider=default", diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index cfafbf48873..f1985805a26 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -384,6 +384,7 @@ extern const OSSL_DISPATCH ossl_mac_legacy_siphash_signature_functions[]; extern const OSSL_DISPATCH ossl_mac_legacy_poly1305_signature_functions[]; extern const OSSL_DISPATCH ossl_mac_legacy_cmac_signature_functions[]; extern const OSSL_DISPATCH ossl_sm2_signature_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_signature_functions[]; /* 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 6987a122505..a37fc68a66d 100644 --- a/providers/implementations/signature/build.info +++ b/providers/implementations/signature/build.info @@ -6,6 +6,7 @@ $EC_GOAL=../../libdefault.a ../../libfips.a $MAC_GOAL=../../libdefault.a ../../libfips.a $RSA_GOAL=../../libdefault.a ../../libfips.a $SM2_GOAL=../../libdefault.a +$ML_DSA_GOAL=../../libdefault.a IF[{- !$disabled{dsa} -}] SOURCE[$DSA_GOAL]=dsa_sig.c @@ -31,3 +32,7 @@ DEPEND[eddsa_sig.o]=../../common/include/prov/der_ecx.h DEPEND[sm2_sig.o]=../../common/include/prov/der_sm2.h SOURCE[$MAC_GOAL]=mac_legacy_sig.c + +IF[{- !$disabled{ml-dsa} -}] + SOURCE[$DSA_GOAL]=ml_dsa_sig.c +ENDIF diff --git a/providers/implementations/signature/ml_dsa_sig.c b/providers/implementations/signature/ml_dsa_sig.c new file mode 100644 index 00000000000..d6eadcbb70d --- /dev/null +++ b/providers/implementations/signature/ml_dsa_sig.c @@ -0,0 +1,247 @@ +/* + * 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 /* memset */ +#include +#include +#include +#include +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "crypto/ml_dsa.h" + +#define ML_DSA_ENTROPY_LEN 32 + +#define ML_DSA_MESSAGE_ENCODE_RAW 0 +#define ML_DSA_MESSAGE_ENCODE_PURE 1 + +static OSSL_FUNC_signature_sign_message_init_fn ml_dsa_sign_msg_init; +static OSSL_FUNC_signature_sign_fn ml_dsa_sign; +static OSSL_FUNC_signature_verify_message_init_fn ml_dsa_verify_msg_init; +static OSSL_FUNC_signature_verify_fn ml_dsa_verify; +static OSSL_FUNC_signature_freectx_fn ml_dsa_freectx; +static OSSL_FUNC_signature_set_ctx_params_fn ml_dsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn ml_dsa_settable_ctx_params; + +typedef struct { + ML_DSA_KEY *key; + ML_DSA_CTX *ctx; + uint8_t context_string[ML_DSA_MAX_CONTEXT_STRING_LEN]; + size_t context_string_len; + uint8_t test_entropy[ML_DSA_ENTROPY_LEN]; + size_t test_entropy_len; + int msg_encode; + int deterministic; + OSSL_LIB_CTX *libctx; + char *propq; +} PROV_ML_DSA_CTX; + +static void ml_dsa_freectx(void *vctx) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + + OPENSSL_free(ctx->propq); + ossl_ml_dsa_ctx_free(ctx->ctx); + ossl_ml_dsa_key_free(ctx->key); + OPENSSL_cleanse(ctx->test_entropy, ctx->test_entropy_len); + OPENSSL_free(ctx); +} + +static void *ml_dsa_newctx(void *provctx, const char *alg, const char *propq) +{ + PROV_ML_DSA_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(PROV_ML_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_ml_dsa_ctx_new(alg, ctx->libctx, ctx->propq); + if (ctx->ctx == NULL) + goto err; + ctx->msg_encode = ML_DSA_MESSAGE_ENCODE_PURE; + + return ctx; + err: + ml_dsa_freectx(ctx); + return NULL; +} + +static int ml_dsa_signverify_msg_init(void *vctx, void *vkey, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + ML_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_ml_dsa_key_type_matches(ctx->ctx, key)) + return 0; + if (!ossl_ml_dsa_key_up_ref(vkey)) + return 0; + ossl_ml_dsa_key_free(ctx->key); + ctx->key = vkey; + } + + if (!ml_dsa_set_ctx_params(ctx, params)) + return 0; + return 1; +} + +static int ml_dsa_sign_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + return ml_dsa_signverify_msg_init(vctx, vkey, params, + EVP_PKEY_OP_SIGN, "ML_DSA Sign Init"); +} + +static int ml_dsa_sign(void *vctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *msg, size_t msg_len) +{ + int ret = 0; + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + uint8_t rand_tmp[ML_DSA_ENTROPY_LEN], *rnd = NULL; + + if (!ossl_prov_is_running()) + return 0; + + if (sig != NULL) { + if (ctx->test_entropy_len != 0) { + rnd = ctx->test_entropy; + } else { + rnd = rand_tmp; + + if (ctx->deterministic == 1) + memset(rnd, 0, sizeof(rand_tmp)); + else if (RAND_priv_bytes_ex(ctx->libctx, rnd, sizeof(rand_tmp), 0) <= 0) + return 0; + } + } + ret = ossl_ml_dsa_sign(ctx->ctx, ctx->key, msg, msg_len, + ctx->context_string, ctx->context_string_len, + rnd, sizeof(rand_tmp), ctx->msg_encode, + sig, siglen, sigsize); + if (rnd != ctx->test_entropy) + OPENSSL_cleanse(rand_tmp, sizeof(rand_tmp)); + return ret; +} + +static int ml_dsa_verify_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + return ml_dsa_signverify_msg_init(vctx, vkey, params, EVP_PKEY_OP_VERIFY, + "ML_DSA Verify Init"); +} + +static int ml_dsa_verify(void *vctx, const unsigned char *sig, size_t siglen, + const unsigned char *msg, size_t msg_len) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + return ossl_ml_dsa_verify(ctx->ctx, ctx->key, msg, msg_len, + ctx->context_string, ctx->context_string_len, + ctx->msg_encode, sig, siglen); +} + +static int ml_dsa_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ML_DSA_CTX *pctx = (PROV_ML_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_TEST_ENTROPY); + if (p != NULL) { + void *vp = pctx->test_entropy; + + if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->test_entropy), + &(pctx->test_entropy_len)) + || pctx->test_entropy_len != sizeof(pctx->test_entropy)) { + pctx->test_entropy_len = 0; + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DETERMINISTIC); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->deterministic)) + 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 *ml_dsa_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_octet_string(OSSL_SIGNATURE_PARAM_TEST_ENTROPY, NULL, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, 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 ml_dsa_##fn##_newctx; \ + static void *ml_dsa_##fn##_newctx(void *provctx, const char *propq) \ + { \ + return ml_dsa_newctx(provctx, alg, propq); \ + } \ + const OSSL_DISPATCH ossl_ml_dsa_##fn##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ml_dsa_##fn##_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))ml_dsa_sign_msg_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ml_dsa_sign }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))ml_dsa_verify_msg_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ml_dsa_verify }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ml_dsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))ml_dsa_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ml_dsa_settable_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +MAKE_SIGNATURE_FUNCTIONS("ML-DSA-65", 65); diff --git a/test/ml_dsa.inc b/test/ml_dsa.inc index 273ac7f6741..d407d76a6d0 100644 --- a/test/ml_dsa.inc +++ b/test/ml_dsa.inc @@ -7,19 +7,19 @@ * https://www.openssl.org/source/license.html */ -#define ML_DSA_SIG_TEST_DET_ITEM(alg, name) { \ +#define ML_DSA_SIG_GEN_TEST_DET_ITEM(alg, name) { \ alg, \ - name##_sig_priv, sizeof(name##_sig_priv), \ - name##_sig_msg, sizeof(name##_sig_msg), \ - name##_sig_digest, sizeof(name##_sig_digest), \ + name##_priv, sizeof(name##_priv), \ + name##_msg, sizeof(name##_msg), \ + name##_digest, sizeof(name##_digest), \ } -#define ML_DSA_SIG_TEST_ITEM(alg, name) { \ +#define ML_DSA_SIG_GEN_TEST_ITEM(alg, name) { \ alg, \ - name##_sig_priv, sizeof(name##_sig_priv), \ - name##_sig_msg, sizeof(name##_sig_msg), \ - name##_sig_digest, sizeof(name##_sig_digest), \ - name##_sig_add_random, sizeof(name##_sig_add_random), \ + name##_priv, sizeof(name##_priv), \ + name##_msg, sizeof(name##_msg), \ + name##_digest, sizeof(name##_digest), \ + name##_add_random, sizeof(name##_add_random), \ } #define ML_DSA_KEYGEN_TEST_ITEM(alg, name) { \ @@ -29,7 +29,7 @@ name##_keygen_priv, sizeof(name##_keygen_priv), \ } -typedef struct slh_dsa_sig_test_data_st { +typedef struct ml_dsa_sig_test_data_st { const char *alg; const unsigned char *priv; size_t priv_len; @@ -42,7 +42,7 @@ typedef struct slh_dsa_sig_test_data_st { size_t add_random_len; } ML_DSA_SIG_TEST_DATA; -typedef struct slh_dsa_keygen_test_data_st { +typedef struct ml_dsa_keygen_test_data_st { const char *name; const uint8_t *seed; size_t seed_len; @@ -52,666 +52,284 @@ typedef struct slh_dsa_keygen_test_data_st { size_t priv_len; } ML_DSA_KEYGEN_TEST_DATA; -#if 0 - -static const uint8_t ml_dsa_65_0_sig_priv[] = { - 0x3e, 0x93, 0x5d, 0x3b, 0x7d, 0xb7, 0xee, 0x99, 0x1c, 0xe7, 0x74, 0xfa, 0x5b, 0x93, 0xd9, 0xbe, - 0x0d, 0x10, 0x8b, 0xe3, 0x97, 0xee, 0x17, 0x65, 0x68, 0x29, 0x6e, 0xe7, 0xf2, 0x87, 0x86, 0x00, - 0x7c, 0xaf, 0x11, 0x56, 0x28, 0x0d, 0x77, 0x02, 0x45, 0x61, 0x1b, 0x2d, 0x34, 0x6a, 0x65, 0xff, - 0xc7, 0x35, 0xa3, 0xa2, 0xe1, 0x5b, 0xb2, 0x0c, 0x05, 0x14, 0x3d, 0x92, 0x56, 0x69, 0x1b, 0x2a, - 0xb4, 0x1d, 0x75, 0xa9, 0x0b, 0xc5, 0xe8, 0xf3, 0xb3, 0x0b, 0x6f, 0x5c, 0xd0, 0x5f, 0xd1, 0x89, - 0x92, 0x92, 0x4a, 0x5b, 0x59, 0xa6, 0x5a, 0xf0, 0x6c, 0x0f, 0x00, 0x0e, 0x74, 0xa2, 0x19, 0xdd, - 0x3e, 0x76, 0x04, 0xb5, 0x83, 0x31, 0xdb, 0x0a, 0x0a, 0xe9, 0x01, 0xc2, 0x93, 0x91, 0x79, 0x44, - 0x17, 0xdd, 0x11, 0x0b, 0xc8, 0xaa, 0xf7, 0xf6, 0x2c, 0xfe, 0xfb, 0x34, 0xe0, 0x0e, 0x1d, 0x12, - 0x73, 0x22, 0x46, 0x08, 0x14, 0x03, 0x52, 0x20, 0x33, 0x74, 0x00, 0x78, 0x75, 0x25, 0x61, 0x71, - 0x86, 0x47, 0x15, 0x36, 0x14, 0x76, 0x15, 0x04, 0x43, 0x07, 0x61, 0x35, 0x10, 0x76, 0x82, 0x41, - 0x75, 0x65, 0x75, 0x45, 0x53, 0x62, 0x35, 0x86, 0x60, 0x63, 0x81, 0x22, 0x08, 0x42, 0x16, 0x62, - 0x73, 0x34, 0x13, 0x13, 0x62, 0x65, 0x71, 0x12, 0x77, 0x38, 0x33, 0x60, 0x04, 0x60, 0x33, 0x26, - 0x03, 0x34, 0x70, 0x80, 0x17, 0x50, 0x14, 0x26, 0x13, 0x46, 0x43, 0x76, 0x06, 0x43, 0x42, 0x02, - 0x03, 0x45, 0x04, 0x42, 0x24, 0x04, 0x00, 0x84, 0x30, 0x31, 0x14, 0x57, 0x08, 0x20, 0x78, 0x38, - 0x67, 0x26, 0x76, 0x16, 0x51, 0x13, 0x00, 0x68, 0x85, 0x05, 0x78, 0x01, 0x60, 0x36, 0x48, 0x78, - 0x83, 0x01, 0x03, 0x71, 0x10, 0x55, 0x61, 0x80, 0x08, 0x11, 0x17, 0x45, 0x15, 0x13, 0x75, 0x37, - 0x57, 0x68, 0x60, 0x07, 0x43, 0x81, 0x21, 0x37, 0x55, 0x73, 0x62, 0x36, 0x57, 0x33, 0x33, 0x16, - 0x14, 0x32, 0x31, 0x20, 0x36, 0x61, 0x35, 0x55, 0x42, 0x51, 0x74, 0x65, 0x66, 0x18, 0x51, 0x86, - 0x84, 0x41, 0x42, 0x40, 0x50, 0x08, 0x81, 0x27, 0x16, 0x20, 0x32, 0x87, 0x60, 0x10, 0x83, 0x14, - 0x37, 0x16, 0x20, 0x05, 0x64, 0x57, 0x66, 0x35, 0x04, 0x75, 0x20, 0x17, 0x70, 0x22, 0x18, 0x00, - 0x63, 0x75, 0x25, 0x20, 0x16, 0x46, 0x42, 0x71, 0x44, 0x15, 0x32, 0x77, 0x78, 0x15, 0x10, 0x52, - 0x00, 0x86, 0x42, 0x80, 0x85, 0x73, 0x38, 0x00, 0x26, 0x88, 0x34, 0x57, 0x43, 0x60, 0x64, 0x23, - 0x41, 0x22, 0x61, 0x82, 0x36, 0x02, 0x55, 0x63, 0x05, 0x85, 0x41, 0x67, 0x42, 0x01, 0x72, 0x30, - 0x48, 0x78, 0x86, 0x40, 0x11, 0x28, 0x13, 0x15, 0x06, 0x54, 0x40, 0x37, 0x74, 0x84, 0x14, 0x24, - 0x45, 0x33, 0x05, 0x30, 0x32, 0x21, 0x14, 0x87, 0x53, 0x00, 0x86, 0x88, 0x03, 0x13, 0x32, 0x25, - 0x40, 0x82, 0x07, 0x82, 0x42, 0x58, 0x48, 0x40, 0x28, 0x61, 0x77, 0x30, 0x16, 0x07, 0x17, 0x67, - 0x88, 0x01, 0x71, 0x21, 0x68, 0x44, 0x33, 0x04, 0x01, 0x73, 0x75, 0x55, 0x55, 0x75, 0x06, 0x83, - 0x65, 0x18, 0x13, 0x04, 0x15, 0x66, 0x08, 0x43, 0x54, 0x58, 0x86, 0x62, 0x41, 0x86, 0x42, 0x25, - 0x00, 0x64, 0x74, 0x74, 0x73, 0x03, 0x18, 0x12, 0x37, 0x40, 0x21, 0x67, 0x32, 0x82, 0x48, 0x68, - 0x27, 0x60, 0x50, 0x38, 0x05, 0x53, 0x24, 0x71, 0x02, 0x54, 0x45, 0x58, 0x17, 0x62, 0x61, 0x00, - 0x00, 0x02, 0x30, 0x66, 0x48, 0x26, 0x10, 0x31, 0x72, 0x48, 0x81, 0x76, 0x53, 0x11, 0x34, 0x30, - 0x68, 0x46, 0x02, 0x47, 0x23, 0x44, 0x83, 0x11, 0x74, 0x02, 0x30, 0x68, 0x00, 0x42, 0x14, 0x88, - 0x50, 0x53, 0x68, 0x37, 0x73, 0x05, 0x52, 0x57, 0x13, 0x45, 0x64, 0x33, 0x70, 0x46, 0x15, 0x44, - 0x61, 0x07, 0x80, 0x18, 0x37, 0x73, 0x45, 0x10, 0x78, 0x17, 0x11, 0x80, 0x10, 0x45, 0x82, 0x14, - 0x78, 0x07, 0x55, 0x68, 0x73, 0x67, 0x00, 0x47, 0x48, 0x85, 0x18, 0x65, 0x43, 0x85, 0x28, 0x48, - 0x74, 0x45, 0x13, 0x81, 0x58, 0x06, 0x20, 0x11, 0x37, 0x57, 0x11, 0x28, 0x03, 0x08, 0x30, 0x74, - 0x31, 0x46, 0x84, 0x32, 0x02, 0x37, 0x10, 0x00, 0x37, 0x47, 0x77, 0x66, 0x10, 0x57, 0x12, 0x11, - 0x82, 0x41, 0x16, 0x14, 0x56, 0x21, 0x22, 0x45, 0x65, 0x60, 0x34, 0x18, 0x61, 0x54, 0x01, 0x70, - 0x04, 0x46, 0x41, 0x76, 0x33, 0x43, 0x26, 0x74, 0x04, 0x02, 0x83, 0x27, 0x64, 0x30, 0x87, 0x66, - 0x44, 0x58, 0x51, 0x50, 0x40, 0x60, 0x64, 0x05, 0x76, 0x12, 0x44, 0x00, 0x28, 0x13, 0x53, 0x64, - 0x73, 0x23, 0x21, 0x48, 0x21, 0x03, 0x38, 0x57, 0x54, 0x55, 0x23, 0x61, 0x07, 0x74, 0x62, 0x50, - 0x68, 0x10, 0x61, 0x22, 0x68, 0x31, 0x80, 0x22, 0x16, 0x78, 0x26, 0x03, 0x62, 0x52, 0x12, 0x01, - 0x30, 0x28, 0x63, 0x40, 0x67, 0x12, 0x35, 0x06, 0x82, 0x47, 0x52, 0x71, 0x63, 0x58, 0x57, 0x22, - 0x05, 0x71, 0x52, 0x53, 0x43, 0x25, 0x88, 0x36, 0x27, 0x50, 0x54, 0x82, 0x77, 0x41, 0x87, 0x76, - 0x21, 0x32, 0x64, 0x32, 0x02, 0x05, 0x80, 0x78, 0x47, 0x10, 0x35, 0x38, 0x75, 0x77, 0x28, 0x68, - 0x34, 0x18, 0x78, 0x36, 0x30, 0x51, 0x85, 0x61, 0x58, 0x44, 0x30, 0x44, 0x54, 0x88, 0x40, 0x05, - 0x31, 0x68, 0x83, 0x87, 0x28, 0x70, 0x44, 0x86, 0x52, 0x12, 0x68, 0x77, 0x24, 0x77, 0x03, 0x10, - 0x31, 0x71, 0x56, 0x32, 0x77, 0x01, 0x25, 0x82, 0x18, 0x27, 0x46, 0x82, 0x81, 0x54, 0x51, 0x48, - 0x68, 0x64, 0x12, 0x46, 0x73, 0x56, 0x36, 0x01, 0x06, 0x47, 0x87, 0x86, 0x42, 0x75, 0x17, 0x18, - 0x28, 0x48, 0x46, 0x63, 0x86, 0x61, 0x64, 0x60, 0x70, 0x73, 0x11, 0x67, 0x56, 0x54, 0x32, 0x71, - 0x02, 0x52, 0x86, 0x46, 0x24, 0x85, 0x52, 0x17, 0x50, 0x50, 0x55, 0x43, 0x80, 0x13, 0x31, 0x30, - 0x60, 0x54, 0x83, 0x62, 0x71, 0x38, 0x56, 0x84, 0x43, 0x07, 0x40, 0x22, 0x18, 0x70, 0x57, 0x48, - 0x42, 0x32, 0x37, 0x54, 0x61, 0x55, 0x01, 0x20, 0x57, 0x74, 0x63, 0x00, 0x28, 0x14, 0x01, 0x30, - 0x83, 0x53, 0x40, 0x21, 0x23, 0x74, 0x75, 0x85, 0x03, 0x08, 0x46, 0x70, 0x47, 0x43, 0x52, 0x62, - 0x47, 0x12, 0x36, 0x26, 0x63, 0x05, 0x06, 0x34, 0x08, 0x11, 0x52, 0x74, 0x53, 0x55, 0x42, 0x16, - 0x44, 0x20, 0x05, 0x35, 0x44, 0x84, 0x28, 0x43, 0x63, 0x23, 0x28, 0x23, 0x27, 0x63, 0x50, 0x34, - 0x05, 0x30, 0x68, 0x65, 0x14, 0x50, 0x32, 0x68, 0x84, 0x02, 0x01, 0x88, 0x82, 0x07, 0x30, 0x27, - 0x57, 0x17, 0x68, 0x37, 0x06, 0x05, 0x42, 0x44, 0x27, 0x64, 0x37, 0x18, 0x68, 0x82, 0x64, 0x80, - 0x15, 0x31, 0x46, 0x06, 0x82, 0x25, 0x43, 0x20, 0x65, 0x84, 0x41, 0x48, 0x04, 0x17, 0x08, 0x14, - 0x80, 0x17, 0x32, 0x21, 0x04, 0x83, 0x03, 0x52, 0x67, 0x15, 0x88, 0x25, 0x27, 0x87, 0x05, 0x43, - 0x27, 0x04, 0x88, 0x73, 0x32, 0x75, 0x48, 0x83, 0x44, 0x41, 0x48, 0x28, 0x02, 0x17, 0x60, 0x80, - 0x18, 0x15, 0x57, 0x12, 0x12, 0x20, 0x18, 0x13, 0x87, 0x43, 0x40, 0x35, 0x65, 0x43, 0x31, 0x04, - 0x86, 0x02, 0x64, 0x22, 0x08, 0x68, 0x26, 0x30, 0x12, 0x13, 0x86, 0x54, 0x60, 0x57, 0x45, 0x51, - 0x54, 0x71, 0x65, 0x83, 0x34, 0x64, 0x57, 0x13, 0x31, 0x34, 0x23, 0x33, 0x32, 0x43, 0x61, 0x54, - 0x17, 0x70, 0x18, 0x81, 0x54, 0x25, 0x67, 0x76, 0x00, 0x25, 0x70, 0x45, 0x28, 0x22, 0x67, 0x10, - 0x53, 0x62, 0x22, 0x86, 0x15, 0x15, 0x72, 0x58, 0x88, 0x71, 0x37, 0x86, 0x18, 0x20, 0x38, 0x62, - 0x70, 0x86, 0x70, 0x03, 0x28, 0x72, 0x33, 0x17, 0x27, 0x75, 0x11, 0x30, 0x05, 0x61, 0x33, 0x83, - 0x88, 0x55, 0x58, 0x56, 0x07, 0x87, 0x60, 0x11, 0x01, 0x06, 0x76, 0x27, 0x56, 0x32, 0x62, 0x52, - 0x03, 0x12, 0x11, 0x58, 0x64, 0x13, 0x26, 0x87, 0x58, 0x54, 0x12, 0x81, 0x31, 0x67, 0x23, 0x82, - 0x38, 0x10, 0x20, 0x35, 0x31, 0x85, 0x78, 0x00, 0x15, 0x70, 0x51, 0x12, 0x32, 0x60, 0x25, 0x80, - 0x02, 0x26, 0x56, 0x30, 0x74, 0x58, 0x04, 0x44, 0x60, 0x47, 0x00, 0x20, 0x76, 0x00, 0x18, 0x36, - 0x43, 0x03, 0x17, 0x76, 0x08, 0x26, 0x61, 0x22, 0x08, 0x66, 0x11, 0x16, 0x32, 0x10, 0x31, 0x07, - 0x37, 0x21, 0x46, 0x82, 0x30, 0x46, 0x82, 0x02, 0x44, 0x45, 0x54, 0x12, 0x42, 0x15, 0x80, 0x87, - 0x50, 0x43, 0x25, 0x71, 0x54, 0x23, 0x13, 0x87, 0x81, 0x73, 0x56, 0x10, 0x08, 0x82, 0x74, 0x74, - 0x32, 0x50, 0x11, 0x52, 0x56, 0x53, 0x07, 0x26, 0x71, 0x61, 0x43, 0x01, 0x20, 0x11, 0x85, 0x07, - 0x74, 0x43, 0x08, 0x57, 0x48, 0x66, 0x23, 0x37, 0x00, 0x32, 0x68, 0x27, 0x55, 0x23, 0x38, 0x46, - 0x60, 0x67, 0x21, 0x14, 0x65, 0x54, 0x62, 0x57, 0x66, 0x07, 0x01, 0x67, 0x42, 0x21, 0x14, 0x82, - 0x84, 0x15, 0x33, 0x10, 0x84, 0x42, 0x42, 0x63, 0x47, 0x35, 0x38, 0x21, 0x44, 0x18, 0x32, 0x25, - 0x24, 0x10, 0x03, 0x47, 0x15, 0x75, 0x54, 0x37, 0x15, 0x56, 0x18, 0x43, 0x71, 0x61, 0x68, 0x71, - 0x58, 0x56, 0x04, 0x20, 0x71, 0x10, 0x83, 0x01, 0x01, 0x28, 0x01, 0x65, 0x23, 0x45, 0x04, 0x52, - 0x55, 0x01, 0x67, 0x35, 0x80, 0x56, 0x65, 0x61, 0x22, 0x03, 0x23, 0x80, 0x88, 0x22, 0x55, 0x55, - 0x23, 0x52, 0x53, 0x07, 0x11, 0x16, 0x30, 0x68, 0x08, 0x17, 0x34, 0x33, 0x03, 0x16, 0x10, 0x57, - 0x28, 0x82, 0x37, 0x21, 0x84, 0x86, 0x01, 0x54, 0x86, 0x31, 0x35, 0x21, 0x26, 0x81, 0x74, 0x57, - 0x72, 0x53, 0x83, 0x11, 0x18, 0x31, 0x31, 0x35, 0x85, 0x41, 0x04, 0x05, 0x58, 0x57, 0x47, 0x35, - 0x41, 0x04, 0x88, 0x45, 0x44, 0x26, 0x06, 0x58, 0x81, 0x44, 0x64, 0x70, 0x86, 0x46, 0x80, 0x18, - 0x80, 0x26, 0x24, 0x62, 0x87, 0x81, 0x56, 0x64, 0x52, 0x00, 0x33, 0x26, 0x82, 0x07, 0x53, 0x08, - 0x52, 0x64, 0x47, 0x20, 0x76, 0x47, 0x14, 0x77, 0x07, 0x41, 0x54, 0x01, 0x41, 0x06, 0x57, 0x54, - 0x17, 0x12, 0x22, 0x38, 0x54, 0x08, 0x03, 0x55, 0x58, 0x63, 0x76, 0x84, 0x37, 0x66, 0x20, 0x48, - 0x77, 0x44, 0x58, 0x47, 0x45, 0x73, 0x65, 0x33, 0x38, 0x74, 0x25, 0x47, 0x34, 0x34, 0x28, 0x07, - 0x35, 0x54, 0x67, 0x66, 0x51, 0x50, 0x87, 0x15, 0x03, 0x06, 0x38, 0x83, 0x83, 0x87, 0x37, 0x63, - 0x03, 0x07, 0x20, 0x28, 0x62, 0x31, 0x05, 0x07, 0x77, 0x65, 0x82, 0x56, 0x53, 0x60, 0x78, 0x01, - 0x33, 0x73, 0x57, 0x37, 0x31, 0x54, 0x30, 0x08, 0x08, 0x05, 0x40, 0x83, 0x75, 0x32, 0x05, 0x42, - 0x65, 0x73, 0x27, 0x25, 0x30, 0x14, 0x03, 0x18, 0x56, 0x81, 0x58, 0x03, 0x40, 0x55, 0x61, 0x82, - 0x75, 0x15, 0x34, 0x15, 0x22, 0x16, 0x78, 0x00, 0x45, 0x27, 0x27, 0x61, 0x13, 0x62, 0x56, 0x55, - 0x68, 0x58, 0x46, 0x11, 0x50, 0x18, 0x67, 0x53, 0x28, 0x00, 0x04, 0x48, 0x88, 0x20, 0x17, 0x23, - 0x21, 0x26, 0x54, 0x30, 0x42, 0x82, 0x78, 0x87, 0x67, 0x41, 0x15, 0x28, 0x05, 0x30, 0x42, 0x54, - 0xef, 0x2d, 0x2b, 0x23, 0x51, 0x89, 0x6f, 0xa8, 0x81, 0xa5, 0xf6, 0x02, 0x2a, 0x2b, 0xd4, 0x20, - 0xa3, 0xd3, 0x9b, 0x3b, 0x3c, 0x4a, 0x13, 0x8a, 0x6f, 0x29, 0x9a, 0x62, 0x19, 0x79, 0x03, 0x11, - 0x11, 0x51, 0x5b, 0x40, 0x2d, 0x90, 0x40, 0x36, 0x1d, 0x5c, 0x6e, 0x2d, 0xb1, 0x87, 0x20, 0x4f, - 0xad, 0xc4, 0x7a, 0x8a, 0xf0, 0x73, 0x82, 0xc4, 0x37, 0xe8, 0x55, 0x36, 0xea, 0xbd, 0x12, 0xb6, - 0x1f, 0x0e, 0x50, 0x67, 0x9a, 0x42, 0xeb, 0x88, 0x6f, 0xdd, 0x3a, 0xbf, 0x04, 0xbb, 0x7c, 0x16, - 0xfe, 0x94, 0x9a, 0xeb, 0x66, 0xb3, 0x0b, 0x68, 0x5a, 0x0c, 0xbe, 0xcb, 0xa9, 0x0e, 0xc1, 0x34, - 0x0c, 0xbb, 0x5f, 0xe2, 0x38, 0x2a, 0x53, 0x4b, 0xa5, 0x8a, 0xfe, 0x4a, 0x8a, 0x29, 0x30, 0x79, - 0xd5, 0x1d, 0x7e, 0xc7, 0xc9, 0xec, 0x51, 0x56, 0x97, 0x72, 0x6c, 0x23, 0xaa, 0xb9, 0x81, 0x5d, - 0xac, 0x18, 0x46, 0x96, 0xff, 0x52, 0xae, 0x3c, 0x02, 0xff, 0x71, 0x60, 0x81, 0xfd, 0x07, 0xab, - 0x10, 0xb6, 0x53, 0xc3, 0xd4, 0xb4, 0x7c, 0x32, 0xbf, 0xb5, 0x39, 0x2d, 0x0c, 0xd2, 0x2d, 0x5b, - 0x99, 0x26, 0x4c, 0x5b, 0x58, 0xf6, 0x5a, 0x5e, 0x52, 0xc8, 0xd7, 0x99, 0xc1, 0x7a, 0x04, 0x73, - 0xa9, 0x62, 0xa6, 0x84, 0x2c, 0xfd, 0x25, 0x5c, 0xeb, 0xc0, 0xf2, 0x0e, 0x9a, 0xcd, 0x7e, 0xfb, - 0xc8, 0xa7, 0x22, 0x6e, 0xa9, 0x98, 0xf0, 0x14, 0xc3, 0xe7, 0x15, 0x35, 0xa6, 0xd8, 0xab, 0xbe, - 0xa5, 0xe8, 0x48, 0x7e, 0xf2, 0xa4, 0x51, 0x6a, 0x4b, 0x83, 0x54, 0xb1, 0x9c, 0x03, 0xb9, 0x71, - 0x23, 0x0b, 0xfa, 0xda, 0xe6, 0xc8, 0x0d, 0xd3, 0xd2, 0x34, 0x56, 0x45, 0x53, 0xe2, 0x49, 0x31, - 0x53, 0xfd, 0x09, 0xaa, 0xfb, 0x5f, 0x06, 0xdb, 0x57, 0xb3, 0x88, 0xe0, 0x00, 0xd1, 0x49, 0xc8, - 0xae, 0x52, 0xcb, 0x42, 0x88, 0x61, 0x41, 0x31, 0x6a, 0xa5, 0x7f, 0xbd, 0xce, 0xb7, 0x8f, 0x81, - 0x5e, 0x31, 0x63, 0x0c, 0x8a, 0x55, 0x7f, 0xc5, 0xf5, 0x4c, 0xce, 0x95, 0xe1, 0xcb, 0x5f, 0x25, - 0xae, 0x1b, 0x62, 0xd2, 0xa1, 0xf3, 0x90, 0x44, 0x4f, 0xcd, 0x77, 0xeb, 0x74, 0x57, 0x85, 0xf5, - 0xb3, 0x6c, 0xbe, 0x14, 0x33, 0xe5, 0x55, 0x21, 0x16, 0x5b, 0xac, 0x3b, 0x2b, 0x00, 0x46, 0x22, - 0x4e, 0xba, 0xfb, 0x4c, 0x9e, 0x74, 0xe6, 0x34, 0x3a, 0xd9, 0x4a, 0xa3, 0x7a, 0xe9, 0x32, 0xbf, - 0xc7, 0x70, 0x80, 0x37, 0x86, 0x68, 0x73, 0x92, 0xb2, 0x6c, 0xa7, 0xc4, 0x71, 0x24, 0xe8, 0x8b, - 0x8b, 0x99, 0x71, 0x34, 0x53, 0xfb, 0x22, 0xc2, 0x10, 0xeb, 0x6e, 0xfa, 0xbb, 0xf4, 0xf0, 0x7d, - 0x47, 0xc0, 0x15, 0x43, 0x56, 0x4f, 0x6d, 0x3c, 0x62, 0x33, 0xb9, 0x13, 0xec, 0x45, 0x53, 0xe8, - 0x07, 0x5c, 0x97, 0xa6, 0xde, 0xe1, 0xcf, 0x8c, 0xfe, 0xa5, 0xc1, 0x67, 0xdd, 0xa8, 0x25, 0x8c, - 0xa6, 0x9f, 0xee, 0xdd, 0x8d, 0x46, 0xc5, 0x2f, 0x3a, 0x51, 0x05, 0x5e, 0x89, 0x0b, 0x23, 0x51, - 0xcc, 0x3a, 0x18, 0x98, 0xcf, 0x6f, 0xdf, 0x51, 0xf8, 0x5f, 0x01, 0x45, 0x3e, 0x64, 0xb1, 0xe6, - 0xcc, 0x83, 0xe2, 0x8a, 0x6c, 0x22, 0xc8, 0xbe, 0xd8, 0x0f, 0xb7, 0xb4, 0xd2, 0x7d, 0x47, 0x5b, - 0xa4, 0x1b, 0x9d, 0xf5, 0x6e, 0xfd, 0xf8, 0xfc, 0x4e, 0xdd, 0xcb, 0x45, 0x3f, 0x65, 0xd8, 0x36, - 0xf3, 0xbc, 0x96, 0xe4, 0x3f, 0x0a, 0xc0, 0xb5, 0x64, 0x5b, 0x4f, 0x8d, 0x30, 0x6d, 0x72, 0x72, - 0xd5, 0x4f, 0x86, 0xc9, 0x0f, 0xbb, 0xe4, 0xca, 0x89, 0x7d, 0xf1, 0xf5, 0x67, 0x48, 0xae, 0x6d, - 0x0b, 0x0d, 0xb3, 0x27, 0x7a, 0x73, 0x85, 0xee, 0x75, 0x61, 0xb5, 0x70, 0xd2, 0x70, 0xc9, 0xac, - 0xe0, 0x6d, 0x2a, 0x00, 0x44, 0xbe, 0x7c, 0x8b, 0xd0, 0x1f, 0x3c, 0xe0, 0x8e, 0xc3, 0x65, 0x61, - 0x91, 0x1f, 0x01, 0x55, 0x92, 0x24, 0x32, 0xe9, 0x04, 0x54, 0x86, 0xa6, 0x02, 0xaa, 0x4f, 0x06, - 0xa0, 0x2c, 0x30, 0x0f, 0x8a, 0x71, 0x50, 0x4a, 0x13, 0x4e, 0xaf, 0xff, 0xc0, 0x51, 0xf1, 0xd4, - 0xd4, 0x2d, 0x88, 0x1e, 0x30, 0xfd, 0xd5, 0xdf, 0xaa, 0xfb, 0xf9, 0xe9, 0xbf, 0x74, 0x5a, 0xe0, - 0xf7, 0x0f, 0xc2, 0x6f, 0xd4, 0x04, 0xfa, 0x79, 0x7d, 0x9e, 0xaa, 0x76, 0xd9, 0x06, 0x5c, 0x64, - 0x97, 0x85, 0xf5, 0xfb, 0xc1, 0x34, 0x69, 0x0c, 0x7d, 0x8b, 0xd6, 0x39, 0x2d, 0x4e, 0xe9, 0x9c, - 0x48, 0x8b, 0x9e, 0xe1, 0x5d, 0xef, 0x92, 0x27, 0x86, 0x8a, 0x19, 0x9e, 0xf3, 0x58, 0xbf, 0x08, - 0xef, 0x8d, 0xdc, 0x5e, 0x45, 0x36, 0x52, 0x17, 0xa4, 0xad, 0xe0, 0xd3, 0x6a, 0xf7, 0xb0, 0xd3, - 0xd7, 0x44, 0xc3, 0xea, 0x3b, 0xb2, 0x6b, 0x56, 0xb9, 0xce, 0xb3, 0x74, 0x71, 0x0f, 0x75, 0x8b, - 0xc7, 0xaa, 0x68, 0xbb, 0x34, 0x6e, 0xc2, 0xdb, 0x5b, 0x5e, 0x7f, 0xfd, 0xd5, 0xf0, 0xb4, 0xce, - 0x9f, 0x25, 0xf0, 0x3e, 0x72, 0xa9, 0xe2, 0xf5, 0xdb, 0x15, 0xe3, 0xb4, 0xe9, 0x22, 0xce, 0x09, - 0xa5, 0x7e, 0x25, 0x80, 0xcb, 0xc2, 0x87, 0x90, 0x37, 0x03, 0xe1, 0x65, 0xbd, 0x7f, 0x42, 0xd4, - 0x27, 0x4a, 0xab, 0x82, 0x0e, 0xb7, 0x1e, 0x84, 0x20, 0xcd, 0x47, 0xf8, 0x76, 0x24, 0x72, 0x7e, - 0x79, 0xac, 0x50, 0x7b, 0x09, 0x3d, 0x22, 0xc9, 0xe6, 0xe7, 0x61, 0xdf, 0x4a, 0x30, 0x82, 0xae, - 0xce, 0x10, 0x95, 0xd3, 0xa5, 0xf1, 0x01, 0xec, 0x01, 0x9f, 0x2b, 0x84, 0xac, 0xd4, 0xbd, 0xd1, - 0xf5, 0x37, 0x9c, 0xac, 0x8d, 0x7b, 0x88, 0x08, 0x48, 0x5c, 0x6e, 0xd1, 0xac, 0xd5, 0xb1, 0xa2, - 0x45, 0x59, 0x51, 0x08, 0xb4, 0x83, 0x60, 0x04, 0xd6, 0xa6, 0xd6, 0x12, 0x94, 0xc1, 0xc8, 0xeb, - 0xad, 0x65, 0xa0, 0xe7, 0x42, 0xb9, 0x8c, 0xcc, 0x7d, 0x1d, 0x27, 0x60, 0x53, 0x6b, 0x6e, 0xbd, - 0x19, 0xa8, 0x8d, 0x17, 0xc3, 0x59, 0x8e, 0x86, 0xe5, 0xa2, 0x2c, 0x0e, 0xa0, 0x7b, 0xe6, 0x71, - 0xde, 0x3a, 0xef, 0x09, 0x6a, 0xae, 0x6d, 0x3b, 0x71, 0xaa, 0xdb, 0x38, 0x83, 0x9a, 0xcf, 0xa5, - 0x59, 0x81, 0x8e, 0xb4, 0x3a, 0x34, 0x18, 0x57, 0x6f, 0x3b, 0x42, 0xdc, 0xd0, 0xe1, 0x28, 0xaa, - 0x02, 0x22, 0x34, 0x21, 0x8e, 0x61, 0x61, 0x69, 0xf0, 0xba, 0x38, 0x47, 0xf0, 0xef, 0x62, 0xff, - 0xf4, 0xef, 0x7d, 0x4d, 0xe8, 0x03, 0x68, 0x2d, 0x40, 0xa5, 0xc3, 0xb7, 0xd8, 0xf4, 0xd5, 0xfa, - 0x14, 0x44, 0x47, 0x0f, 0x1b, 0x66, 0xde, 0x18, 0x7b, 0x50, 0xfe, 0x42, 0x69, 0x81, 0x28, 0xaa, - 0x93, 0x35, 0x0c, 0x20, 0xe7, 0xd3, 0x7f, 0x61, 0x95, 0x37, 0xc2, 0x20, 0xd7, 0x8c, 0x5e, 0xbe, - 0xf8, 0x9c, 0xa0, 0xf1, 0x1c, 0x7c, 0x7a, 0x93, 0xd8, 0x3f, 0x28, 0xd8, 0x3a, 0x17, 0x21, 0x6a, - 0xdc, 0x45, 0x0e, 0x25, 0xf3, 0xeb, 0x00, 0xd9, 0xf3, 0x65, 0xc9, 0xf5, 0xed, 0x69, 0xdd, 0x08, - 0xb6, 0x8e, 0x0b, 0xc9, 0x78, 0x0f, 0x34, 0xd1, 0x50, 0x32, 0x9e, 0x49, 0x06, 0xd3, 0x0a, 0x04, - 0xe4, 0x7b, 0x6b, 0x6d, 0x50, 0x8d, 0xf9, 0xa9, 0x45, 0xed, 0x83, 0xe3, 0x9d, 0xbf, 0xf9, 0xb0, - 0xc1, 0x36, 0x48, 0x6d, 0x01, 0xa9, 0x1d, 0x11, 0xd3, 0x7b, 0x5a, 0xdb, 0x8a, 0x23, 0xd3, 0x05, - 0xb7, 0x06, 0x09, 0xf1, 0x26, 0x58, 0x8c, 0x8f, 0x77, 0xb6, 0xd0, 0x61, 0x4d, 0xb5, 0x36, 0x4c, - 0xf3, 0x6e, 0xbb, 0xe9, 0xac, 0x59, 0xd4, 0xaf, 0xa1, 0x8d, 0xf5, 0x30, 0xac, 0x5e, 0xd8, 0xb5, - 0x8d, 0x68, 0xd0, 0xcb, 0x7b, 0xf9, 0x1d, 0x65, 0xb0, 0xf3, 0x92, 0xad, 0x4b, 0x40, 0x89, 0x92, - 0x54, 0x9f, 0x8a, 0x8f, 0xbe, 0xca, 0xf2, 0x56, 0x52, 0xf0, 0xf6, 0xc4, 0xab, 0x6f, 0x32, 0xd5, - 0xb1, 0x47, 0xe8, 0xd8, 0x52, 0x86, 0xeb, 0xf9, 0x6a, 0x5d, 0x57, 0xb1, 0x7a, 0x98, 0x99, 0xe8, - 0x54, 0xeb, 0x45, 0x44, 0xbf, 0xce, 0x30, 0x36, 0x2f, 0x7b, 0xba, 0x0d, 0x2f, 0xe4, 0x01, 0x02, - 0xed, 0xe0, 0x8e, 0xe9, 0xf4, 0x3d, 0x07, 0x09, 0xcf, 0xc8, 0x9b, 0x8e, 0x33, 0xfd, 0x5f, 0x38, - 0x15, 0xa7, 0x71, 0xc5, 0x6d, 0xb8, 0xd7, 0xc7, 0x88, 0xc4, 0x9e, 0xef, 0xf5, 0x3a, 0xab, 0xc5, - 0xe3, 0x41, 0xf1, 0x84, 0xf9, 0x2a, 0xda, 0xa2, 0x25, 0x12, 0x2d, 0x2d, 0x95, 0xe3, 0xbf, 0x03, - 0xa2, 0x7d, 0xa0, 0xc1, 0x04, 0xc6, 0xf4, 0x4a, 0x3e, 0x6e, 0xac, 0x0c, 0xbc, 0x2f, 0x20, 0x66, - 0xd1, 0x7a, 0xa7, 0x4a, 0x58, 0x46, 0x44, 0xea, 0xe6, 0xe2, 0xb6, 0x9c, 0x32, 0xea, 0x11, 0x6c, - 0x91, 0xd8, 0xd6, 0xd5, 0x9c, 0x97, 0x44, 0xfc, 0x22, 0xd3, 0x8e, 0xe5, 0xcf, 0xd0, 0x91, 0x0d, - 0xd3, 0x05, 0x78, 0x8b, 0xca, 0x66, 0x14, 0x63, 0xaf, 0x04, 0x09, 0x0b, 0x57, 0x21, 0x5e, 0x70, - 0x28, 0x50, 0xe8, 0x5a, 0xa1, 0xc6, 0x60, 0x75, 0xb1, 0x5b, 0x9c, 0x9d, 0x6e, 0x93, 0x25, 0xa6, - 0x34, 0xcb, 0x04, 0xa6, 0xc7, 0x27, 0x1c, 0xef, 0xd9, 0xde, 0xfd, 0xa2, 0xd4, 0x29, 0x2e, 0xf1, - 0x53, 0x59, 0x98, 0xe5, 0xa6, 0xe6, 0x5c, 0x4e, 0x51, 0xdb, 0x49, 0xe2, 0x7f, 0xe2, 0x98, 0x6a, - 0xf0, 0x2f, 0x4b, 0xea, 0xfb, 0xe1, 0x74, 0xbd, 0x75, 0xe8, 0x99, 0x86, 0x51, 0xe6, 0x10, 0x95, - 0x81, 0x88, 0x82, 0x08, 0x45, 0xaa, 0xeb, 0x23, 0x9d, 0x2d, 0x15, 0x2b, 0x2c, 0x25, 0x97, 0xd0, - 0x4d, 0x76, 0x14, 0xa4, 0x43, 0x9f, 0x60, 0x48, 0xd2, 0xb3, 0x61, 0xfd, 0x9c, 0x55, 0x8f, 0x40, - 0xce, 0x0d, 0x2d, 0x86, 0xd7, 0xf7, 0x68, 0x46, 0xeb, 0x81, 0xdf, 0xb4, 0xe8, 0x33, 0x12, 0x7c, - 0x87, 0x09, 0x5d, 0xc2, 0xaa, 0xd0, 0xd7, 0xca, 0x4f, 0xfd, 0x18, 0x9c, 0x5e, 0x24, 0xa0, 0xf9, - 0xcd, 0xad, 0x4a, 0x55, 0x98, 0xf3, 0x27, 0x35, 0x14, 0x70, 0x3a, 0xdb, 0x5d, 0x12, 0xe7, 0x96, - 0x6e, 0x51, 0xa8, 0x56, 0xa6, 0x88, 0xcb, 0x6e, 0x25, 0xcf, 0x8c, 0x72, 0x4b, 0xd9, 0xed, 0xdc, - 0xf5, 0x55, 0xb9, 0x0c, 0x65, 0xe2, 0x8e, 0x07, 0x02, 0x2b, 0xdb, 0x2d, 0x75, 0x69, 0xcc, 0x46, - 0xdf, 0x61, 0x3e, 0xa1, 0xe7, 0xfc, 0x1d, 0x88, 0xb0, 0x25, 0x94, 0xa4, 0x64, 0x38, 0x7d, 0xae, - 0xbd, 0x04, 0x62, 0xa5, 0xf9, 0xc8, 0xbb, 0x46, 0xd5, 0x25, 0xf9, 0xbf, 0x8b, 0x3a, 0xf7, 0xfc, - 0x0d, 0x0b, 0x84, 0xdf, 0x55, 0xb2, 0xaf, 0x9e, 0x3e, 0x82, 0xa1, 0x30, 0x57, 0x26, 0xb1, 0xc6, - 0xd6, 0x03, 0x96, 0xfd, 0x08, 0x6b, 0x01, 0xbe, 0x69, 0xf9, 0x1c, 0x75, 0x2c, 0x5d, 0x0a, 0x58, - 0x75, 0x40, 0xc9, 0x19, 0xfa, 0x8e, 0xae, 0x2f, 0x5d, 0x3d, 0x72, 0xa1, 0x4a, 0xe9, 0xd5, 0xe0, - 0xb4, 0xdd, 0x4c, 0x8e, 0xe8, 0x08, 0x43, 0x42, 0x03, 0x3c, 0x93, 0xa8, 0x60, 0x52, 0xad, 0xe3, - 0xa2, 0xa6, 0x8e, 0x4c, 0x7b, 0x5d, 0xd5, 0x99, 0xc5, 0x68, 0xc9, 0x93, 0x5b, 0xe5, 0x9a, 0xb3, - 0x8a, 0xbe, 0x35, 0x56, 0xfe, 0x81, 0x30, 0x63, 0x58, 0xd5, 0xf1, 0x3c, 0x1c, 0x50, 0xc8, 0xe5, - 0xd3, 0x2f, 0x23, 0x02, 0x42, 0xcf, 0x52, 0xe3, 0x0c, 0x43, 0xd8, 0x8c, 0x50, 0xfa, 0x03, 0x0c, - 0xa5, 0xfd, 0xfa, 0x88, 0xda, 0x86, 0x9f, 0x31, 0xb2, 0x9a, 0x95, 0x29, 0x9e, 0xce, 0x1e, 0x4c, - 0xe8, 0x57, 0x5b, 0x6a, 0x63, 0x31, 0x20, 0xea, 0x76, 0xb4, 0x3f, 0x30, 0xe9, 0x51, 0x77, 0x28, - 0x70, 0x8d, 0xe1, 0xe7, 0x3a, 0x27, 0x69, 0x9d, 0x45, 0x1c, 0x14, 0x3d, 0xbf, 0x33, 0x56, 0x84, - 0xa6, 0x43, 0xf5, 0x44, 0x75, 0x6b, 0x94, 0xc5, 0x46, 0x59, 0x39, 0x72, 0xc3, 0x54, 0x3e, 0xc6, - 0x5e, 0xf6, 0x87, 0xca, 0x3f, 0x07, 0x30, 0x2e, 0x67, 0x5f, 0xe0, 0x06, 0x5c, 0x15, 0x26, 0xd1, - 0x35, 0x3d, 0x02, 0x77, 0x78, 0xe2, 0xbd, 0x04, 0xee, 0xa3, 0x1d, 0x40, 0x09, 0x70, 0x1a, 0xe7, - 0xb2, 0x24, 0x1e, 0x32, 0xe1, 0xf9, 0x42, 0x7b, 0xf8, 0x20, 0xcf, 0x55, 0xa4, 0x65, 0x24, 0x89, - 0x93, 0x11, 0x4a, 0x71, 0x45, 0x61, 0x53, 0x84, 0xcf, 0xb4, 0xa2, 0x37, 0xc0, 0xc3, 0x7f, 0x5c, - 0x2c, 0x1b, 0x3a, 0xf7, 0xa6, 0x28, 0x67, 0xf0, 0xda, 0x8e, 0x46, 0x14, 0xde, 0x7b, 0xff, 0x6f, - 0x00, 0x03, 0x4a, 0xe6, 0x28, 0xe6, 0x4d, 0xb9, 0xfd, 0x96, 0x52, 0x8a, 0x78, 0x8d, 0x98, 0xfb, - 0xe6, 0x12, 0x11, 0xd1, 0xf2, 0x2e, 0x37, 0xb9, 0x78, 0x87, 0x3e, 0xe7, 0x8a, 0x6e, 0x87, 0x1c, - 0x52, 0x65, 0x3d, 0x1e, 0xe0, 0xe0, 0x17, 0x46, 0x99, 0xff, 0x71, 0xb0, 0x78, 0x8e, 0x75, 0x61, - 0x93, 0x1a, 0xa7, 0x09, 0x39, 0xfe, 0x1d, 0x58, 0x5d, 0x73, 0x35, 0x09, 0x30, 0x35, 0x07, 0x80, - 0x1b, 0x87, 0x18, 0x9c, 0xa1, 0x7b, 0xa3, 0x06, 0x0a, 0x4b, 0xe8, 0x5f, 0x9a, 0xbe, 0x6c, 0xce, - 0x14, 0x2f, 0x8e, 0x16, 0x45, 0x1e, 0xb3, 0xc5, 0x1a, 0x43, 0x33, 0x82, 0x76, 0x41, 0xa9, 0x36, - 0x1b, 0xf6, 0x79, 0xe6, 0x65, 0xf0, 0xe0, 0xe1, 0x1f, 0x65, 0x7a, 0xb9, 0x27, 0x7d, 0x5e, 0x3a, - 0x9e, 0xbb, 0x8d, 0xa3, 0x3b, 0xca, 0x8d, 0x37, 0x25, 0x91, 0x66, 0xdc, 0x8c, 0x68, 0xa7, 0xa6, - 0x92, 0xb5, 0x70, 0x88, 0x32, 0x76, 0x45, 0x0a, 0x56, 0xd9, 0x84, 0xb7, 0x74, 0xa1, 0x0a, 0xf0, - 0xba, 0x76, 0x69, 0x71, 0xdb, 0x37, 0x55, 0xab, 0x5e, 0xde, 0x5b, 0xb8, 0xcd, 0x62, 0x92, 0x55, - 0x52, 0x08, 0xb3, 0x78, 0x72, 0xef, 0xa2, 0xe7, 0xe4, 0x27, 0x46, 0x57, 0x6f, 0x72, 0x5c, 0xa9, - 0xe9, 0xff, 0x5a, 0x69, 0x03, 0x94, 0x5c, 0xb0, 0x8e, 0x71, 0x5d, 0x4b, 0xf4, 0x68, 0xab, 0x84, - 0x72, 0x10, 0xe3, 0x0c, 0xd8, 0xb2, 0x89, 0xd2, 0xfb, 0x74, 0x57, 0x26, 0xd7, 0x6a, 0x58, 0x02, - 0xa4, 0xc7, 0x4d, 0xf5, 0x44, 0xe8, 0x35, 0xd6, 0x1d, 0xfe, 0xf6, 0x80, 0x84, 0x6e, 0xbc, 0x5f, - 0x71, 0x5f, 0x07, 0x1f, 0xa0, 0xcf, 0x2b, 0xc3, 0xad, 0x0b, 0x0f, 0x01, 0x6c, 0x9a, 0x67, 0x16, - 0x2b, 0x4f, 0x92, 0x6c, 0x32, 0xea, 0x6d, 0xc7, 0xc5, 0x48, 0xe8, 0x59, 0xb7, 0x2a, 0xb7, 0x3c, - 0x7a, 0x29, 0xc4, 0xf0, 0x6d, 0x60, 0x9c, 0x32, 0x84, 0xc7, 0x84, 0xe4, 0x0c, 0xd4, 0x60, 0xec, - 0x14, 0x9b, 0xdc, 0x17, 0xe6, 0x14, 0x60, 0x3f, 0x26, 0xbe, 0xb2, 0xe8, 0xae, 0x8a, 0x0c, 0xe2, - 0x1e, 0x00, 0xea, 0x3d, 0x18, 0xce, 0x18, 0x65, 0xbe, 0x6e, 0xb0, 0xb9, 0xe9, 0xc7, 0x9c, 0x17, - 0x00, 0x49, 0x49, 0x24, 0x18, 0xce, 0x00, 0x9b, 0xaf, 0xba, 0xcd, 0x4b, 0x8a, 0xe5, 0x55, 0x37, - 0xd8, 0xcd, 0x88, 0x39, 0xeb, 0x42, 0xfa, 0x69, 0x21, 0x28, 0x2b, 0x32, 0xef, 0xd2, 0xa7, 0x6b, - 0x1a, 0x96, 0x5d, 0xe1, 0xbe, 0x3c, 0xb0, 0x00, 0x10, 0x69, 0xd4, 0xd8, 0x0d, 0x67, 0x15, 0xf8, - 0xe1, 0x73, 0xbc, 0x8b, 0xb9, 0x72, 0xf2, 0x30, 0x20, 0xe0, 0xba, 0x2d, 0xdf, 0x3a, 0x2d, 0x39, - 0x0f, 0x11, 0xf1, 0xf5, 0xbe, 0x9f, 0x61, 0x34, 0xf4, 0x27, 0x6c, 0xf6, 0x3c, 0x5d, 0xd6, 0xa1, - 0x54, 0xe1, 0x33, 0xd9, 0xe1, 0x12, 0x6f, 0xe5, 0x5c, 0x51, 0x13, 0xf5, 0xe3, 0x48, 0x5a, 0xd0, - 0x51, 0xb4, 0xc6, 0x13, 0xe3, 0x2a, 0xd3, 0xbc, 0x07, 0x93, 0xb0, 0xd9, 0x55, 0xfe, 0xe7, 0x9b, - 0x13, 0x92, 0xae, 0x2e, 0xdf, 0x04, 0x87, 0x53, 0xba, 0xf8, 0x2e, 0xb4, 0xb7, 0x39, 0x74, 0x6c, - 0x07, 0x5c, 0xf8, 0x47, 0x29, 0xa9, 0x0e, 0xf6, 0x06, 0x85, 0xc3, 0xf7, 0x83, 0x3b, 0xb0, 0xd0, - 0x08, 0x95, 0x92, 0x4e, 0x5a, 0x8b, 0xcb, 0xcb, 0x7b, 0xaf, 0x41, 0xc7, 0x8c, 0x37, 0x05, 0xc4, - 0x8c, 0x28, 0x55, 0x99, 0x68, 0xc9, 0x47, 0xbc, 0x6f, 0xba, 0x0e, 0x96, 0x40, 0xf8, 0x84, 0x5a, - 0x45, 0xc5, 0xaa, 0xa0, 0x18, 0x84, 0x2f, 0xca, 0x46, 0xa2, 0x5d, 0x68, 0xdc, 0xcb, 0xde, 0x8b, - 0xad, 0xeb, 0xa3, 0x41, 0xf8, 0x96, 0x9a, 0x3c, 0x83, 0x33, 0x7e, 0xf3, 0xa6, 0x38, 0x3c, 0xfa, - 0x4c, 0xf9, 0xb0, 0x93, 0xf7, 0xc8, 0x1c, 0xd8, 0x94, 0x70, 0x59, 0xcc, 0x6d, 0xb3, 0x50, 0xbe, - 0x20, 0xe8, 0x08, 0x0a, 0x32, 0xc3, 0x56, 0xbb, 0x81, 0x3a, 0x71, 0x45, 0x57, 0xae, 0x66, 0xe6, - 0xdf, 0x11, 0x0a, 0xff, 0xb7, 0xb1, 0x1d, 0x95, 0xa8, 0x2f, 0x47, 0x14, 0xd0, 0x9c, 0x94, 0x55, - 0x54, 0xdc, 0xee, 0x53, 0xb3, 0xa0, 0x6f, 0x08, 0x60, 0xbe, 0x89, 0xe7, 0xb1, 0x94, 0x59, 0xcd, - 0x06, 0x11, 0x54, 0xb9, 0xf8, 0x6e, 0xd7, 0x70, 0xac, 0x27, 0x1d, 0x72, 0x72, 0xa8, 0xd0, 0xf4, - 0x0b, 0xac, 0x2b, 0xe0, 0xf9, 0x50, 0x64, 0xdf, 0x20, 0x24, 0x6f, 0xf3, 0xaa, 0xf3, 0x10, 0x0d, - 0xaa, 0x75, 0xf6, 0x8c, 0xaa, 0x6c, 0x53, 0x2e, 0x11, 0x81, 0xd6, 0x70, 0xd9, 0x4a, 0x68, 0x63, - 0x9a, 0x45, 0x57, 0xd5, 0xfa, 0x10, 0xab, 0x8a, 0x01, 0x6e, 0xbb, 0x94, 0xb3, 0xb3, 0x67, 0x05, - 0x8a, 0xf4, 0x05, 0x41, 0xfe, 0xf8, 0x8f, 0x49, 0x7e, 0xda, 0x04, 0x0b, 0x6d, 0x92, 0x7a, 0x3c, - 0x1f, 0x03, 0x4d, 0x6c, 0x24, 0x72, 0xf8, 0xa9, 0x0e, 0xac, 0xaf, 0xb6, 0x12, 0x42, 0xe9, 0x6f, - 0xfa, 0x47, 0xd1, 0x59, 0x8d, 0x61, 0x07, 0xc1, 0xb4, 0xb6, 0x70, 0x3e, 0x83, 0x06, 0x7a, 0x81, - 0x7e, 0xfa, 0x96, 0x40, 0xa6, 0xa1, 0x27, 0x7e, 0x59, 0xfd, 0xad, 0x28, 0xde, 0xb2, 0x9a, 0x70, - 0xe3, 0x1e, 0xc8, 0x2b, 0xfd, 0xdf, 0x96, 0xdf, 0xbf, 0x77, 0x56, 0xfb, 0x6d, 0x83, 0x68, 0xf9, - 0xe3, 0x40, 0x06, 0x59, 0x97, 0xb5, 0x5b, 0x00, 0xb2, 0x20, 0x57, 0xe7, 0x01, 0x4c, 0xa5, 0x55, - 0xfb, 0xe5, 0x56, 0x3e, 0xee, 0xdd, 0x4b, 0x03, 0x98, 0x35, 0xf6, 0x0f, 0x75, 0x3e, 0x98, 0x10, - 0xb5, 0x41, 0x0a, 0xdf, 0x69, 0x95, 0x15, 0x08, 0x01, 0x04, 0xae, 0xc8, 0x12, 0xc3, 0xec, 0x29, - 0x99, 0x28, 0xc6, 0x6c, 0x49, 0x8b, 0x0e, 0x26, 0xb1, 0x71, 0x9c, 0x69, 0x0b, 0xf5, 0xa4, 0x24, - 0x15, 0xa6, 0xf6, 0x3a, 0x92, 0x78, 0xdf, 0xa4, 0x30, 0x38, 0x0a, 0x82, 0x1b, 0xad, 0x0a, 0x2f, - 0x83, 0x77, 0x23, 0x5a, 0x26, 0x0a, 0x0b, 0xc9, 0xb3, 0x3e, 0x49, 0xfb, 0x98, 0x0e, 0xfd, 0x77, - 0x0b, 0xa9, 0x7f, 0xfe, 0x16, 0x18, 0x3d, 0x6b, 0x1c, 0x24, 0xd0, 0xd1, 0xc3, 0xf4, 0x37, 0xa1, +static const uint8_t ml_dsa_65_0_sig_gen_priv[] = { + 0x7d, 0x9b, 0xac, 0xa9, 0xc8, 0xd5, 0xe3, 0x02, 0xbf, 0x5c, 0xe4, 0xc8, 0x5b, 0x76, 0x85, 0x38, + 0x8c, 0xec, 0xd8, 0x2d, 0x72, 0xec, 0x25, 0x99, 0x76, 0xf4, 0xcb, 0x65, 0xc3, 0x60, 0xd7, 0x4b, + 0x7a, 0xa8, 0xb2, 0x3c, 0xa3, 0xc9, 0xd7, 0x86, 0xfa, 0x3d, 0x94, 0x9a, 0x9d, 0xfc, 0x16, 0x00, + 0xc8, 0x21, 0xb8, 0x08, 0xf0, 0xbe, 0xb3, 0x8f, 0x81, 0x5d, 0x76, 0x88, 0x92, 0x81, 0x59, 0xd7, + 0xd9, 0xe9, 0xc1, 0xf8, 0x0f, 0xbe, 0xae, 0xb0, 0x71, 0x8b, 0x4a, 0x27, 0xe5, 0xae, 0x16, 0xaf, + 0x32, 0x5f, 0x23, 0x62, 0x53, 0x91, 0x64, 0xd1, 0xb0, 0xf2, 0x82, 0x13, 0x16, 0x84, 0x08, 0x9e, + 0x9c, 0x61, 0x44, 0x2c, 0x8c, 0x6e, 0xf0, 0x38, 0x18, 0xe4, 0xfa, 0xb3, 0x83, 0x49, 0x8a, 0x91, + 0xf1, 0xa6, 0xba, 0x3d, 0x11, 0x38, 0xfa, 0x2e, 0x68, 0x63, 0xe7, 0xbf, 0xc3, 0x7c, 0x76, 0x8b, + 0x80, 0x88, 0x11, 0x02, 0x10, 0x68, 0x34, 0x53, 0x58, 0x85, 0x56, 0x78, 0x54, 0x24, 0x33, 0x44, + 0x50, 0x22, 0x11, 0x75, 0x75, 0x22, 0x26, 0x75, 0x23, 0x16, 0x20, 0x88, 0x46, 0x65, 0x34, 0x44, + 0x12, 0x53, 0x60, 0x27, 0x63, 0x32, 0x71, 0x88, 0x22, 0x66, 0x27, 0x58, 0x13, 0x85, 0x65, 0x25, + 0x65, 0x41, 0x41, 0x51, 0x78, 0x43, 0x80, 0x76, 0x27, 0x22, 0x45, 0x66, 0x34, 0x14, 0x26, 0x28, + 0x23, 0x14, 0x58, 0x73, 0x88, 0x08, 0x65, 0x38, 0x24, 0x32, 0x87, 0x65, 0x25, 0x67, 0x87, 0x54, + 0x46, 0x65, 0x87, 0x18, 0x06, 0x53, 0x18, 0x22, 0x24, 0x81, 0x71, 0x71, 0x56, 0x44, 0x61, 0x78, + 0x52, 0x25, 0x33, 0x06, 0x32, 0x36, 0x76, 0x05, 0x34, 0x73, 0x56, 0x52, 0x68, 0x17, 0x65, 0x86, + 0x83, 0x22, 0x13, 0x36, 0x50, 0x52, 0x84, 0x64, 0x20, 0x07, 0x33, 0x11, 0x44, 0x75, 0x20, 0x43, + 0x04, 0x70, 0x88, 0x67, 0x88, 0x43, 0x65, 0x02, 0x85, 0x68, 0x68, 0x76, 0x47, 0x54, 0x30, 0x47, + 0x58, 0x68, 0x57, 0x73, 0x06, 0x41, 0x26, 0x12, 0x40, 0x86, 0x60, 0x22, 0x70, 0x61, 0x46, 0x70, + 0x06, 0x62, 0x64, 0x16, 0x05, 0x03, 0x30, 0x36, 0x28, 0x80, 0x88, 0x00, 0x42, 0x83, 0x71, 0x12, + 0x43, 0x31, 0x15, 0x86, 0x23, 0x77, 0x30, 0x24, 0x15, 0x00, 0x40, 0x25, 0x84, 0x66, 0x35, 0x42, + 0x42, 0x63, 0x13, 0x22, 0x18, 0x28, 0x37, 0x26, 0x52, 0x06, 0x68, 0x21, 0x45, 0x23, 0x85, 0x41, + 0x37, 0x77, 0x58, 0x02, 0x62, 0x31, 0x30, 0x21, 0x73, 0x75, 0x15, 0x56, 0x16, 0x87, 0x44, 0x86, + 0x34, 0x22, 0x72, 0x45, 0x38, 0x41, 0x65, 0x43, 0x33, 0x50, 0x44, 0x26, 0x00, 0x14, 0x14, 0x07, + 0x00, 0x08, 0x02, 0x23, 0x63, 0x56, 0x62, 0x74, 0x61, 0x30, 0x85, 0x40, 0x42, 0x51, 0x50, 0x87, + 0x00, 0x26, 0x85, 0x10, 0x28, 0x61, 0x28, 0x54, 0x10, 0x48, 0x65, 0x25, 0x32, 0x66, 0x37, 0x35, + 0x31, 0x84, 0x25, 0x32, 0x82, 0x22, 0x80, 0x15, 0x07, 0x84, 0x75, 0x04, 0x11, 0x17, 0x21, 0x28, + 0x44, 0x87, 0x81, 0x41, 0x82, 0x50, 0x33, 0x87, 0x31, 0x50, 0x75, 0x18, 0x31, 0x80, 0x57, 0x82, + 0x17, 0x03, 0x51, 0x46, 0x50, 0x68, 0x07, 0x16, 0x71, 0x20, 0x74, 0x78, 0x63, 0x44, 0x01, 0x48, + 0x42, 0x55, 0x32, 0x63, 0x50, 0x63, 0x31, 0x85, 0x46, 0x33, 0x51, 0x18, 0x05, 0x60, 0x73, 0x75, + 0x81, 0x67, 0x71, 0x64, 0x27, 0x31, 0x48, 0x33, 0x76, 0x20, 0x65, 0x05, 0x06, 0x31, 0x87, 0x28, + 0x81, 0x76, 0x25, 0x58, 0x13, 0x11, 0x47, 0x45, 0x87, 0x14, 0x34, 0x74, 0x20, 0x06, 0x31, 0x17, + 0x57, 0x67, 0x30, 0x68, 0x36, 0x70, 0x04, 0x54, 0x67, 0x31, 0x65, 0x20, 0x14, 0x46, 0x44, 0x05, + 0x52, 0x35, 0x14, 0x05, 0x57, 0x03, 0x70, 0x76, 0x50, 0x20, 0x23, 0x62, 0x27, 0x65, 0x01, 0x55, + 0x73, 0x57, 0x76, 0x37, 0x05, 0x75, 0x12, 0x74, 0x58, 0x24, 0x60, 0x10, 0x02, 0x22, 0x64, 0x66, + 0x34, 0x62, 0x42, 0x44, 0x23, 0x21, 0x37, 0x65, 0x31, 0x02, 0x60, 0x01, 0x88, 0x43, 0x16, 0x55, + 0x28, 0x66, 0x17, 0x03, 0x71, 0x70, 0x74, 0x06, 0x23, 0x47, 0x11, 0x35, 0x07, 0x03, 0x53, 0x01, + 0x54, 0x53, 0x73, 0x86, 0x51, 0x05, 0x17, 0x15, 0x08, 0x62, 0x48, 0x54, 0x88, 0x73, 0x46, 0x41, + 0x51, 0x01, 0x23, 0x01, 0x82, 0x05, 0x33, 0x34, 0x30, 0x48, 0x44, 0x18, 0x02, 0x45, 0x31, 0x12, + 0x05, 0x55, 0x68, 0x78, 0x26, 0x72, 0x10, 0x22, 0x16, 0x81, 0x70, 0x76, 0x71, 0x13, 0x05, 0x63, + 0x11, 0x12, 0x27, 0x84, 0x03, 0x46, 0x16, 0x34, 0x45, 0x34, 0x67, 0x56, 0x38, 0x17, 0x40, 0x51, + 0x74, 0x62, 0x56, 0x77, 0x44, 0x21, 0x10, 0x02, 0x86, 0x02, 0x88, 0x20, 0x46, 0x13, 0x25, 0x65, + 0x41, 0x01, 0x45, 0x22, 0x56, 0x16, 0x05, 0x65, 0x07, 0x38, 0x11, 0x15, 0x10, 0x65, 0x24, 0x86, + 0x11, 0x30, 0x03, 0x23, 0x62, 0x84, 0x70, 0x58, 0x28, 0x18, 0x75, 0x20, 0x16, 0x54, 0x20, 0x71, + 0x01, 0x41, 0x26, 0x52, 0x10, 0x77, 0x23, 0x23, 0x35, 0x73, 0x40, 0x16, 0x51, 0x16, 0x44, 0x52, + 0x10, 0x43, 0x24, 0x28, 0x53, 0x14, 0x11, 0x53, 0x85, 0x78, 0x27, 0x07, 0x68, 0x73, 0x46, 0x22, + 0x77, 0x47, 0x14, 0x25, 0x58, 0x68, 0x72, 0x56, 0x25, 0x18, 0x58, 0x66, 0x51, 0x68, 0x28, 0x82, + 0x62, 0x88, 0x22, 0x52, 0x25, 0x38, 0x24, 0x81, 0x36, 0x23, 0x46, 0x23, 0x17, 0x32, 0x17, 0x16, + 0x42, 0x64, 0x38, 0x03, 0x14, 0x86, 0x71, 0x27, 0x18, 0x17, 0x47, 0x11, 0x06, 0x24, 0x50, 0x53, + 0x75, 0x51, 0x83, 0x04, 0x51, 0x02, 0x41, 0x72, 0x42, 0x70, 0x13, 0x40, 0x83, 0x13, 0x85, 0x35, + 0x86, 0x86, 0x76, 0x10, 0x31, 0x75, 0x24, 0x88, 0x15, 0x58, 0x11, 0x28, 0x51, 0x74, 0x78, 0x43, + 0x01, 0x44, 0x67, 0x83, 0x81, 0x46, 0x03, 0x45, 0x27, 0x46, 0x40, 0x08, 0x81, 0x10, 0x43, 0x01, + 0x51, 0x53, 0x40, 0x32, 0x34, 0x11, 0x22, 0x06, 0x06, 0x18, 0x73, 0x82, 0x84, 0x81, 0x13, 0x53, + 0x12, 0x73, 0x23, 0x86, 0x82, 0x14, 0x21, 0x64, 0x17, 0x66, 0x84, 0x77, 0x31, 0x60, 0x42, 0x86, + 0x40, 0x81, 0x17, 0x70, 0x83, 0x60, 0x21, 0x00, 0x88, 0x16, 0x72, 0x22, 0x62, 0x73, 0x55, 0x42, + 0x17, 0x74, 0x25, 0x02, 0x40, 0x11, 0x65, 0x67, 0x34, 0x74, 0x22, 0x20, 0x20, 0x81, 0x77, 0x65, + 0x43, 0x27, 0x65, 0x18, 0x38, 0x23, 0x77, 0x31, 0x11, 0x56, 0x76, 0x20, 0x50, 0x78, 0x44, 0x58, + 0x37, 0x42, 0x57, 0x63, 0x85, 0x57, 0x15, 0x74, 0x65, 0x45, 0x68, 0x51, 0x01, 0x71, 0x83, 0x01, + 0x14, 0x00, 0x56, 0x04, 0x24, 0x10, 0x64, 0x07, 0x67, 0x43, 0x52, 0x20, 0x14, 0x44, 0x20, 0x78, + 0x10, 0x45, 0x32, 0x47, 0x11, 0x83, 0x47, 0x10, 0x61, 0x27, 0x75, 0x23, 0x54, 0x83, 0x70, 0x82, + 0x56, 0x71, 0x83, 0x66, 0x03, 0x60, 0x56, 0x83, 0x48, 0x56, 0x35, 0x50, 0x47, 0x18, 0x78, 0x72, + 0x86, 0x62, 0x51, 0x72, 0x66, 0x12, 0x85, 0x41, 0x01, 0x12, 0x73, 0x41, 0x05, 0x24, 0x03, 0x06, + 0x43, 0x11, 0x31, 0x52, 0x44, 0x42, 0x81, 0x28, 0x78, 0x43, 0x65, 0x15, 0x72, 0x05, 0x41, 0x47, + 0x85, 0x33, 0x78, 0x48, 0x65, 0x07, 0x28, 0x14, 0x47, 0x72, 0x33, 0x21, 0x37, 0x37, 0x78, 0x41, + 0x02, 0x62, 0x31, 0x71, 0x27, 0x84, 0x78, 0x70, 0x72, 0x70, 0x82, 0x07, 0x44, 0x05, 0x34, 0x64, + 0x23, 0x52, 0x01, 0x57, 0x38, 0x40, 0x05, 0x62, 0x51, 0x38, 0x05, 0x48, 0x55, 0x83, 0x23, 0x50, + 0x12, 0x40, 0x57, 0x55, 0x53, 0x47, 0x37, 0x64, 0x78, 0x87, 0x61, 0x76, 0x87, 0x05, 0x66, 0x13, + 0x23, 0x65, 0x28, 0x82, 0x34, 0x13, 0x68, 0x76, 0x22, 0x03, 0x58, 0x30, 0x16, 0x13, 0x27, 0x66, + 0x64, 0x32, 0x82, 0x67, 0x03, 0x00, 0x84, 0x62, 0x40, 0x34, 0x20, 0x24, 0x67, 0x88, 0x06, 0x72, + 0x76, 0x83, 0x33, 0x55, 0x84, 0x83, 0x30, 0x82, 0x53, 0x52, 0x62, 0x70, 0x51, 0x27, 0x10, 0x45, + 0x10, 0x24, 0x66, 0x53, 0x18, 0x06, 0x66, 0x41, 0x12, 0x10, 0x17, 0x02, 0x76, 0x26, 0x67, 0x02, + 0x31, 0x85, 0x28, 0x74, 0x41, 0x05, 0x21, 0x04, 0x50, 0x73, 0x53, 0x31, 0x52, 0x41, 0x36, 0x76, + 0x11, 0x07, 0x43, 0x81, 0x63, 0x54, 0x51, 0x01, 0x83, 0x47, 0x83, 0x66, 0x01, 0x44, 0x75, 0x56, + 0x27, 0x35, 0x01, 0x60, 0x46, 0x02, 0x70, 0x60, 0x13, 0x54, 0x52, 0x64, 0x27, 0x54, 0x27, 0x56, + 0x17, 0x02, 0x13, 0x37, 0x64, 0x22, 0x27, 0x21, 0x52, 0x08, 0x38, 0x32, 0x65, 0x55, 0x53, 0x87, + 0x71, 0x67, 0x88, 0x08, 0x75, 0x47, 0x64, 0x61, 0x26, 0x52, 0x25, 0x53, 0x84, 0x67, 0x27, 0x63, + 0x06, 0x88, 0x33, 0x42, 0x46, 0x42, 0x76, 0x04, 0x75, 0x45, 0x83, 0x48, 0x13, 0x22, 0x14, 0x16, + 0x60, 0x41, 0x67, 0x12, 0x61, 0x18, 0x43, 0x23, 0x44, 0x26, 0x17, 0x22, 0x66, 0x44, 0x67, 0x35, + 0x80, 0x12, 0x51, 0x23, 0x38, 0x06, 0x08, 0x50, 0x20, 0x07, 0x20, 0x22, 0x13, 0x71, 0x44, 0x84, + 0x43, 0x88, 0x25, 0x03, 0x61, 0x52, 0x61, 0x64, 0x48, 0x88, 0x22, 0x28, 0x85, 0x32, 0x38, 0x50, + 0x34, 0x36, 0x65, 0x13, 0x46, 0x73, 0x74, 0x26, 0x22, 0x61, 0x37, 0x45, 0x41, 0x16, 0x52, 0x18, + 0x06, 0x22, 0x43, 0x84, 0x54, 0x71, 0x67, 0x80, 0x08, 0x42, 0x81, 0x34, 0x36, 0x44, 0x73, 0x66, + 0x15, 0x22, 0x12, 0x33, 0x28, 0x13, 0x01, 0x66, 0x32, 0x40, 0x63, 0x20, 0x45, 0x87, 0x58, 0x56, + 0x45, 0x71, 0x65, 0x60, 0x76, 0x27, 0x82, 0x57, 0x14, 0x41, 0x43, 0x50, 0x87, 0x80, 0x11, 0x18, + 0x83, 0x35, 0x00, 0x40, 0x47, 0x21, 0x87, 0x65, 0x13, 0x26, 0x24, 0x17, 0x22, 0x88, 0x55, 0x06, + 0x06, 0x88, 0x43, 0x61, 0x43, 0x84, 0x40, 0x77, 0x17, 0x83, 0x78, 0x23, 0x23, 0x33, 0x34, 0x57, + 0x76, 0x03, 0x58, 0x31, 0x38, 0x60, 0x71, 0x57, 0x75, 0x36, 0x07, 0x18, 0x16, 0x03, 0x24, 0x88, + 0x44, 0x02, 0x15, 0x25, 0x51, 0x86, 0x10, 0x38, 0x25, 0x36, 0x00, 0x24, 0x36, 0x62, 0x87, 0x07, + 0x66, 0x08, 0x30, 0x48, 0x21, 0x35, 0x50, 0x83, 0x27, 0x52, 0x32, 0x34, 0x52, 0x14, 0x13, 0x05, + 0x81, 0x37, 0x64, 0x02, 0x67, 0x36, 0x50, 0x80, 0x46, 0x38, 0x80, 0x37, 0x52, 0x67, 0x72, 0x17, + 0x14, 0x64, 0x46, 0x72, 0x51, 0x68, 0x18, 0x38, 0x56, 0x18, 0x53, 0x55, 0x42, 0x84, 0x24, 0x38, + 0x63, 0x67, 0x53, 0x41, 0x74, 0x12, 0x61, 0x67, 0x38, 0x46, 0x68, 0x76, 0x35, 0x35, 0x22, 0x24, + 0x86, 0x66, 0x70, 0x01, 0x20, 0x62, 0x33, 0x51, 0x17, 0x87, 0x21, 0x72, 0x04, 0x64, 0x48, 0x28, + 0x20, 0x87, 0x42, 0x72, 0x41, 0x56, 0x85, 0x13, 0x76, 0x28, 0x48, 0x16, 0x55, 0x45, 0x58, 0x61, + 0x24, 0x67, 0x78, 0x64, 0x07, 0x61, 0x40, 0x36, 0x77, 0x01, 0x20, 0x83, 0x64, 0x65, 0x08, 0x58, + 0x31, 0x10, 0x08, 0x20, 0x74, 0x45, 0x31, 0x01, 0x20, 0x44, 0x26, 0x41, 0x01, 0x78, 0x43, 0x22, + 0x88, 0x41, 0x21, 0x56, 0x18, 0x27, 0x20, 0x46, 0x51, 0x84, 0x48, 0x51, 0x12, 0x24, 0x03, 0x75, + 0xbf, 0x83, 0x65, 0xfe, 0xe3, 0xa6, 0xc8, 0x11, 0x13, 0x8e, 0x1c, 0xf7, 0x3b, 0xf1, 0xd0, 0xea, + 0x52, 0x5f, 0x2b, 0xdd, 0x49, 0x88, 0xcc, 0x15, 0x7a, 0x97, 0x6b, 0x94, 0x90, 0x22, 0xff, 0xbc, + 0xa9, 0x3b, 0x26, 0x64, 0x02, 0x8f, 0x9d, 0x0a, 0xde, 0x28, 0x00, 0x5c, 0x84, 0xca, 0x7c, 0xc3, + 0x9e, 0xba, 0xbe, 0xa9, 0xc9, 0x20, 0x75, 0x73, 0x4d, 0x9f, 0x83, 0x99, 0x03, 0x53, 0xaf, 0x6d, + 0x72, 0x69, 0x3c, 0xbe, 0x38, 0x65, 0x9c, 0x93, 0x05, 0xc8, 0x04, 0xb8, 0xa7, 0x5c, 0x8b, 0xb1, + 0xc5, 0xff, 0xb6, 0xc5, 0xa6, 0x81, 0x15, 0x5a, 0x2a, 0x65, 0xf8, 0x5c, 0x8d, 0xd4, 0xa1, 0x4c, + 0x6b, 0x61, 0xc4, 0x5d, 0x6d, 0x2b, 0xe9, 0x41, 0x1c, 0x00, 0xa2, 0x9d, 0xaf, 0xdc, 0xf3, 0xd9, + 0xf1, 0xec, 0x1e, 0x2e, 0xf7, 0x52, 0x14, 0x04, 0x3a, 0xc1, 0x20, 0xdc, 0xa4, 0xf2, 0x2a, 0xa7, + 0x35, 0x4a, 0xc2, 0xb6, 0x44, 0x3f, 0xed, 0x11, 0xa0, 0x86, 0xb2, 0x26, 0x30, 0xc1, 0xb4, 0x5b, + 0x2a, 0x37, 0x2f, 0xf2, 0xf1, 0xd0, 0x66, 0x0c, 0x41, 0x08, 0x7f, 0x5c, 0xf8, 0x18, 0xfe, 0xc1, + 0xff, 0x96, 0xf1, 0x8c, 0xd7, 0x10, 0xfa, 0x09, 0xf6, 0x65, 0x89, 0x01, 0x07, 0x08, 0xcf, 0x8d, + 0x29, 0x9e, 0xea, 0xc8, 0x6e, 0x1e, 0x98, 0x9c, 0x2d, 0xdd, 0x60, 0xa2, 0xf6, 0x8d, 0xed, 0xb6, + 0x2b, 0x0e, 0x64, 0x21, 0xd4, 0xdf, 0x28, 0x5a, 0xa2, 0xa3, 0x2f, 0x44, 0x2e, 0x35, 0x68, 0x88, + 0x26, 0xe0, 0xa9, 0xef, 0x9f, 0xc3, 0x2c, 0x32, 0xf0, 0x9a, 0x28, 0x41, 0x9f, 0x37, 0xde, 0x77, + 0x47, 0xdf, 0x9c, 0x86, 0xb5, 0xa5, 0x4a, 0x55, 0x8d, 0x37, 0x4f, 0xc1, 0xc0, 0x83, 0xa4, 0x63, + 0x21, 0xe7, 0x23, 0x7b, 0xf3, 0x74, 0x91, 0x21, 0x4f, 0x54, 0x97, 0x3c, 0xe3, 0xba, 0x8d, 0x72, + 0x29, 0x37, 0x0d, 0xfb, 0x27, 0xdc, 0x68, 0xf3, 0x3e, 0xe6, 0x8b, 0xbe, 0xe3, 0x3d, 0xdb, 0x63, + 0xb3, 0xac, 0x4e, 0x43, 0x46, 0xd6, 0xd9, 0x6a, 0x6a, 0x30, 0x0f, 0x49, 0xad, 0xae, 0xf3, 0x70, + 0xc9, 0x8a, 0xb1, 0xa4, 0x4a, 0x4d, 0x5e, 0x98, 0xc1, 0xb1, 0x9e, 0xd4, 0x33, 0xac, 0x13, 0xc5, + 0xa0, 0x99, 0x89, 0x4c, 0xa3, 0x48, 0x1a, 0x36, 0x82, 0x0e, 0x55, 0x48, 0xa0, 0x4c, 0x63, 0x2b, + 0x09, 0x95, 0xba, 0x97, 0x6a, 0x35, 0x9d, 0xa6, 0xf0, 0x29, 0xde, 0x84, 0x43, 0xd7, 0xab, 0x19, + 0x70, 0x37, 0xaa, 0xac, 0xfc, 0xf2, 0xf6, 0x1d, 0x33, 0xb6, 0x04, 0xdd, 0x3b, 0xb0, 0x9b, 0x26, + 0x52, 0x3b, 0x10, 0x3b, 0x0a, 0xad, 0x0d, 0x21, 0x63, 0xf0, 0x92, 0x94, 0xa4, 0x80, 0x42, 0xe8, + 0xd2, 0x35, 0x99, 0xdf, 0x70, 0x95, 0xe0, 0x09, 0xf3, 0x93, 0xc4, 0xcf, 0x5f, 0x29, 0x5c, 0x3f, + 0x5e, 0x49, 0xf2, 0xae, 0xd1, 0xf2, 0x99, 0x26, 0x65, 0x3c, 0x48, 0xfc, 0xd8, 0xd9, 0x3d, 0xc3, + 0x94, 0x22, 0x40, 0x5c, 0x38, 0xea, 0x9e, 0x28, 0xff, 0x5a, 0xbb, 0xa4, 0xfb, 0x69, 0x9d, 0x15, + 0x8d, 0xb7, 0xf7, 0x0e, 0x30, 0x71, 0x6d, 0xbc, 0x16, 0x27, 0xa2, 0x7e, 0x6a, 0x93, 0xf5, 0x94, + 0xf6, 0x24, 0x18, 0x69, 0x33, 0x2d, 0x4f, 0x3c, 0xf4, 0x17, 0x31, 0xb8, 0x4c, 0x17, 0xdf, 0x19, + 0x5b, 0x7c, 0x7c, 0xa7, 0xe4, 0x56, 0x3a, 0xc9, 0xec, 0x1c, 0xda, 0xb2, 0xd1, 0xe5, 0x2e, 0x5c, + 0x6d, 0x38, 0xb4, 0xc6, 0x3c, 0x39, 0x01, 0xbd, 0x86, 0x0c, 0x03, 0x8a, 0x34, 0x28, 0x4e, 0x71, + 0x83, 0x42, 0x47, 0xd1, 0xd3, 0x85, 0x4a, 0xef, 0x49, 0xe2, 0xbf, 0x6f, 0x67, 0xff, 0xe4, 0x25, + 0x68, 0x69, 0xa4, 0x1b, 0xd4, 0x4b, 0x0f, 0xa2, 0xe0, 0xcf, 0xe0, 0x34, 0x5e, 0xb3, 0x88, 0x6b, + 0xc4, 0x35, 0x6a, 0xee, 0x11, 0xa2, 0x68, 0x38, 0xd4, 0xf2, 0x27, 0x9a, 0xc1, 0x3d, 0x6c, 0xaa, + 0x53, 0xf1, 0x10, 0xac, 0xbb, 0x1f, 0x67, 0x64, 0xbb, 0xe4, 0x7b, 0xf3, 0xc4, 0xe1, 0xf0, 0xb9, + 0xb9, 0x9b, 0x00, 0xb6, 0xb9, 0xe0, 0xc5, 0x00, 0x53, 0xfb, 0x32, 0x17, 0x4b, 0xe3, 0x92, 0x32, + 0x1a, 0x3e, 0x0a, 0x69, 0xbd, 0xc8, 0x74, 0xd3, 0xf3, 0xa4, 0x2a, 0xeb, 0x79, 0xc4, 0xba, 0xb2, + 0x75, 0x37, 0x13, 0xa5, 0xde, 0xce, 0x41, 0x9e, 0x2b, 0xdb, 0xcf, 0xca, 0xc6, 0xcb, 0x74, 0x14, + 0x25, 0xa6, 0x4c, 0xc7, 0xd3, 0x18, 0x0e, 0x09, 0x7b, 0x52, 0x85, 0xe0, 0xb7, 0xbc, 0xbf, 0x9b, + 0x7d, 0x97, 0xde, 0x81, 0xdf, 0x32, 0x27, 0xc2, 0xdd, 0xaa, 0x36, 0x9c, 0x87, 0xbb, 0xf3, 0xf8, + 0x76, 0xad, 0x8b, 0x81, 0xe9, 0x20, 0x8c, 0x2e, 0xae, 0x36, 0x3c, 0x3a, 0x04, 0x34, 0xba, 0xd9, + 0x2f, 0x49, 0xee, 0x7b, 0x6b, 0x80, 0x5b, 0xca, 0x9c, 0xd6, 0xc0, 0xa2, 0xc3, 0xe2, 0x1e, 0x5d, + 0x5d, 0xd9, 0xac, 0x4f, 0x42, 0x7f, 0x62, 0xef, 0xc9, 0x6c, 0x13, 0xf6, 0xee, 0x1e, 0xc7, 0x61, + 0xca, 0xb2, 0x4e, 0x61, 0xdd, 0x6f, 0x50, 0x9d, 0x08, 0xe8, 0xe5, 0xad, 0x4e, 0xda, 0xea, 0xef, + 0x06, 0x79, 0x34, 0x06, 0x77, 0x88, 0x2b, 0xcd, 0xfc, 0xb6, 0x92, 0x8a, 0xe7, 0xc2, 0x05, 0x2d, + 0xe7, 0x11, 0x6c, 0xdb, 0xba, 0xc3, 0x55, 0x1c, 0xe0, 0xda, 0xd8, 0xae, 0x90, 0x9d, 0x79, 0x53, + 0x93, 0xcb, 0x8a, 0x62, 0x92, 0xc7, 0xfb, 0x2c, 0x16, 0xee, 0x62, 0xb8, 0xb5, 0xf0, 0x5c, 0xfd, + 0x55, 0x53, 0xa7, 0x30, 0x39, 0x19, 0xd5, 0x63, 0xb7, 0x61, 0x2e, 0x65, 0xee, 0x69, 0xc9, 0x7a, + 0xe8, 0x0d, 0x3e, 0x40, 0x7a, 0x44, 0x8c, 0x88, 0xd5, 0x08, 0x19, 0x1b, 0x98, 0x82, 0x1f, 0x03, + 0xf3, 0x82, 0x3d, 0xa8, 0x79, 0x30, 0x54, 0xfb, 0x0d, 0x05, 0x1d, 0x9b, 0xd1, 0x2d, 0x09, 0x0e, + 0x2c, 0xfc, 0xca, 0x4d, 0xb2, 0x0c, 0x6a, 0xd5, 0x15, 0xe5, 0xe8, 0x65, 0x82, 0x33, 0xc0, 0x95, + 0x78, 0x1d, 0x5b, 0xb1, 0xd4, 0x0b, 0xc4, 0x7d, 0x3b, 0xde, 0x5a, 0x63, 0x79, 0xcf, 0x03, 0x51, + 0x1b, 0x2a, 0x3c, 0x06, 0xa2, 0xc3, 0x15, 0x73, 0x0a, 0x03, 0x49, 0xf8, 0x35, 0x12, 0x77, 0x8a, + 0x51, 0x23, 0xf7, 0xca, 0x9f, 0xaf, 0xc4, 0x4f, 0x73, 0xb1, 0x25, 0x90, 0x90, 0xf8, 0x74, 0x6f, + 0x1a, 0xe8, 0x68, 0xdf, 0x6d, 0x15, 0x86, 0x36, 0xee, 0xd3, 0x4b, 0x8f, 0xf8, 0x5c, 0xe3, 0xc0, + 0x7f, 0x73, 0x78, 0xed, 0x5a, 0x3e, 0x75, 0x77, 0xa6, 0xfa, 0xe7, 0xcd, 0x3e, 0xa0, 0xde, 0x0e, + 0xe3, 0x57, 0x6c, 0x48, 0x00, 0x8e, 0x2e, 0x4f, 0x8c, 0x41, 0x7f, 0x36, 0x06, 0xbb, 0x1e, 0x9d, + 0xf4, 0x68, 0xe4, 0x0f, 0xe7, 0x4a, 0x3c, 0x69, 0xa6, 0x72, 0xb7, 0x58, 0xf1, 0xc3, 0xac, 0x57, + 0x0e, 0x44, 0xe9, 0xa0, 0x89, 0x36, 0x78, 0x8b, 0x44, 0xa7, 0x69, 0xb2, 0x8d, 0xca, 0xbb, 0xd3, + 0x26, 0x6a, 0x82, 0xcd, 0xa0, 0xcb, 0x6d, 0x20, 0x14, 0xb7, 0x3b, 0xea, 0xcb, 0x33, 0x80, 0x10, + 0x07, 0xc4, 0x0b, 0xf9, 0x2b, 0x64, 0x7f, 0xae, 0x66, 0x31, 0x64, 0x8f, 0x7e, 0x91, 0xda, 0x02, + 0x29, 0x93, 0xa9, 0x19, 0x81, 0x53, 0xb7, 0xb3, 0xc3, 0x1b, 0x48, 0x7c, 0x86, 0x3a, 0x63, 0x9e, + 0xd7, 0xad, 0x4b, 0x23, 0x5f, 0xf3, 0xd4, 0x4e, 0x3a, 0xd5, 0x73, 0x03, 0x69, 0x02, 0xe3, 0xc4, + 0x1a, 0x53, 0xd3, 0x47, 0x04, 0x23, 0x73, 0x04, 0xd2, 0xe4, 0xd7, 0x2a, 0x3f, 0x41, 0xdb, 0x6b, + 0xef, 0xce, 0x9d, 0xa8, 0x94, 0x5c, 0xf5, 0xa9, 0xaa, 0x22, 0x17, 0xa2, 0xb3, 0x6d, 0xd2, 0x95, + 0x6c, 0x3f, 0x4c, 0x81, 0x1b, 0x3d, 0x2a, 0x95, 0xe1, 0xfa, 0x88, 0xff, 0x2e, 0xd9, 0x5a, 0x09, + 0x50, 0xcd, 0xb1, 0xa1, 0x4d, 0xfc, 0x89, 0xed, 0x6e, 0x99, 0x6c, 0x85, 0x4c, 0x1f, 0xf0, 0x56, + 0xa7, 0x3d, 0x7b, 0xfd, 0x1e, 0x8f, 0x08, 0xeb, 0xa0, 0x03, 0xe3, 0xe8, 0x34, 0xc7, 0x15, 0x98, + 0xe4, 0x24, 0x30, 0xb1, 0x5b, 0xc0, 0xf1, 0xff, 0xf6, 0x79, 0x1d, 0x63, 0x9b, 0xd4, 0x09, 0x23, + 0xa2, 0x8f, 0xc4, 0xb7, 0x26, 0x36, 0xaf, 0xf0, 0x1e, 0xf4, 0xf0, 0x1c, 0x48, 0x94, 0xbc, 0x78, + 0xa3, 0xac, 0x69, 0xbc, 0x59, 0x57, 0x60, 0xbe, 0xe8, 0xa2, 0x27, 0xdc, 0x88, 0xad, 0x1e, 0x63, + 0x34, 0x01, 0xb4, 0xea, 0x43, 0xbf, 0xb0, 0x70, 0xab, 0xa4, 0xdd, 0xe3, 0x8f, 0xe0, 0xee, 0x6b, + 0xc4, 0x95, 0x8f, 0x1e, 0x64, 0x81, 0xbf, 0x47, 0x8a, 0x9a, 0xea, 0x4e, 0xc2, 0xae, 0x56, 0x99, + 0x3c, 0xfc, 0x33, 0x97, 0x5c, 0x06, 0xb5, 0x71, 0x45, 0x0d, 0xa0, 0x02, 0x6d, 0xe5, 0x8b, 0x99, + 0xb6, 0xd4, 0x4f, 0x2a, 0xfe, 0xde, 0x58, 0x29, 0x20, 0x43, 0x62, 0x30, 0xa6, 0xe8, 0x1b, 0xf0, + 0x51, 0xbb, 0x54, 0xa4, 0x1a, 0xe6, 0xe6, 0xc3, 0x4b, 0xfa, 0xf1, 0xc7, 0xf0, 0x3a, 0x44, 0xb8, + 0x5a, 0xcd, 0xde, 0x1b, 0x44, 0xde, 0xaa, 0xb8, 0x29, 0x99, 0x05, 0xfb, 0x5a, 0xd9, 0x61, 0xff, + 0x22, 0x4c, 0x71, 0x7e, 0x96, 0xdf, 0xed, 0xec, 0x50, 0xd5, 0xce, 0xa6, 0x46, 0xd2, 0xff, 0xc5, + 0xf7, 0xb6, 0x01, 0x7e, 0xc5, 0x40, 0x92, 0x4d, 0x1c, 0x9f, 0xdb, 0x80, 0x13, 0xf2, 0x95, 0x7f, + 0x27, 0x1c, 0x85, 0x02, 0x79, 0x85, 0xfd, 0x41, 0x84, 0xf2, 0x2e, 0x79, 0x0f, 0x91, 0xcc, 0x7c, + 0x44, 0xc1, 0x89, 0x0a, 0xe8, 0x39, 0x32, 0x48, 0x3a, 0xf2, 0x5f, 0xbc, 0x0a, 0x9c, 0x74, 0x0b, + 0xae, 0x46, 0x7e, 0x2c, 0xa2, 0x4a, 0xa7, 0xe8, 0x6c, 0x41, 0xa9, 0x25, 0x02, 0x60, 0xb3, 0xa4, + 0xf8, 0x05, 0xd6, 0x42, 0x1b, 0x87, 0x72, 0x69, 0x1f, 0x17, 0x2f, 0xb6, 0x39, 0x39, 0x60, 0x6d, + 0x50, 0xd0, 0x3f, 0x7c, 0x4a, 0xa0, 0x9e, 0xbc, 0xc2, 0xe5, 0x28, 0x9b, 0xf4, 0xb9, 0xec, 0x64, + 0xa1, 0x30, 0x78, 0x69, 0xcb, 0x4e, 0xba, 0xb5, 0xc0, 0xab, 0xa2, 0x22, 0xc8, 0x6e, 0x21, 0xd6, + 0xef, 0x32, 0xb8, 0x2b, 0x74, 0x09, 0xb1, 0x0c, 0x67, 0xf7, 0xa7, 0xd5, 0x4c, 0x85, 0x0c, 0x27, + 0x77, 0x28, 0xc1, 0x52, 0x30, 0xc5, 0x6e, 0x3b, 0x0a, 0xa3, 0x75, 0x44, 0x0a, 0xc0, 0xb5, 0x5d, + 0xef, 0xfa, 0xed, 0xe5, 0x2c, 0x2f, 0x84, 0xba, 0x5f, 0xd4, 0x9a, 0x5a, 0xad, 0xa5, 0xaf, 0x6c, + 0x9e, 0xad, 0xc0, 0xe4, 0x13, 0x00, 0x6f, 0x41, 0x49, 0x08, 0x1e, 0xa8, 0xb0, 0x6a, 0x35, 0x90, + 0xe5, 0x5d, 0x55, 0x05, 0x1a, 0xee, 0x47, 0x4e, 0x41, 0x6c, 0x7c, 0x76, 0xf0, 0xc4, 0x1c, 0x37, + 0x2b, 0x59, 0x3a, 0x5a, 0x22, 0xcf, 0x9d, 0x52, 0x0c, 0x59, 0xf9, 0x89, 0x42, 0xb2, 0x87, 0x04, + 0x08, 0xfd, 0xbf, 0xa0, 0x63, 0xfd, 0x14, 0xb6, 0x61, 0x5e, 0xbd, 0xa8, 0x5a, 0xcd, 0x30, 0x20, + 0x38, 0x2e, 0x69, 0x6b, 0x84, 0xb4, 0xe6, 0xe6, 0xe2, 0x8b, 0x1f, 0x6e, 0x27, 0x9e, 0xa4, 0xc5, + 0xac, 0x34, 0x44, 0xe6, 0x70, 0xf5, 0xb1, 0xf0, 0x1a, 0xdc, 0x33, 0x31, 0xb4, 0xe1, 0x3b, 0x13, + 0x87, 0xa0, 0x82, 0x25, 0x1f, 0xe6, 0x3e, 0x39, 0x1e, 0x3a, 0xf0, 0xff, 0xc8, 0xcd, 0x5a, 0x77, + 0xd2, 0xb7, 0x7d, 0xdc, 0x75, 0x06, 0x6e, 0x04, 0xf9, 0xba, 0x89, 0xb6, 0x5e, 0x62, 0xf3, 0xf7, + 0x4b, 0x5e, 0xb5, 0x30, 0x68, 0x44, 0x9a, 0x3a, 0x59, 0xa3, 0xcb, 0x83, 0x51, 0xb5, 0xf8, 0x01, + 0x2f, 0xdc, 0x5b, 0x3f, 0x7a, 0xcb, 0x10, 0xaf, 0x93, 0x3d, 0xd9, 0xfb, 0x5c, 0x09, 0x4b, 0xf0, + 0x85, 0xfe, 0xf0, 0xb2, 0x7d, 0x9b, 0x01, 0xaf, 0xdc, 0x95, 0xb3, 0xb9, 0x66, 0x33, 0xc5, 0x08, + 0x23, 0x69, 0xf0, 0x3b, 0xef, 0xa1, 0x3d, 0x53, 0xff, 0xe3, 0x18, 0x36, 0xd1, 0xe0, 0x2a, 0xde, + 0xff, 0xec, 0x0c, 0xaa, 0x08, 0x40, 0x50, 0xcd, 0x5c, 0xf3, 0x4e, 0x13, 0x62, 0xd4, 0xbc, 0x42, + 0x24, 0x18, 0x53, 0x6a, 0x4f, 0x50, 0x48, 0x63, 0x9a, 0x0f, 0x16, 0x87, 0x43, 0x45, 0x97, 0x33, + 0xcc, 0x11, 0xee, 0x61, 0x6d, 0x81, 0x0d, 0xc9, 0x2b, 0x26, 0x6b, 0xcb, 0x0b, 0x78, 0xf3, 0x22, + 0x69, 0xba, 0x68, 0xf2, 0x64, 0xb6, 0x69, 0x30, 0x2e, 0x8f, 0xc2, 0xe3, 0x5f, 0x99, 0x8e, 0x2b, + 0x04, 0x38, 0xef, 0xa2, 0xfc, 0xa0, 0xb1, 0x4f, 0x0b, 0x33, 0x77, 0x01, 0xa4, 0xfd, 0x50, 0x57, + 0xae, 0x3f, 0x11, 0x05, 0x52, 0x6e, 0xb7, 0x26, 0x38, 0x1c, 0x54, 0xd6, 0x05, 0xd2, 0x1e, 0x9a, + 0x18, 0x87, 0xda, 0x4f, 0x8a, 0xa0, 0x9c, 0x3d, 0x4c, 0xd0, 0xe6, 0x17, 0xbc, 0x07, 0x2f, 0x40, + 0x50, 0xe1, 0x25, 0x76, 0x38, 0x12, 0xd4, 0x62, 0x1f, 0x1a, 0xd8, 0x00, 0x1a, 0xa0, 0x27, 0x54, + 0x6e, 0x47, 0x2f, 0x83, 0x17, 0xe9, 0xf3, 0x7c, 0x27, 0xbf, 0xd4, 0x38, 0x0d, 0x4a, 0x5a, 0x8c, + 0x77, 0xfb, 0x42, 0x6c, 0x3f, 0x13, 0xe4, 0x62, 0xe0, 0x6b, 0x6d, 0x7c, 0x59, 0xc8, 0xda, 0xe7, + 0xec, 0x74, 0x7e, 0x4c, 0x08, 0x82, 0x25, 0x69, 0x42, 0x1c, 0x70, 0xeb, 0x62, 0xdb, 0x7c, 0x3f, + 0x1e, 0xe9, 0xca, 0xf7, 0xce, 0x38, 0x4e, 0xd0, 0xb5, 0x8a, 0x12, 0x2a, 0x2c, 0x0c, 0x66, 0x9d, + 0xc8, 0x43, 0xcd, 0x21, 0xf6, 0x3e, 0x01, 0xce, 0xed, 0x8c, 0xf7, 0xc6, 0x39, 0x18, 0xcc, 0x7c, + 0xd0, 0x4b, 0x80, 0x55, 0x65, 0x61, 0x58, 0x2a, 0x4c, 0xa2, 0x47, 0x32, 0x95, 0x67, 0x00, 0xd5, + 0xad, 0x7e, 0x65, 0xe1, 0xf7, 0xd7, 0x70, 0xec, 0x84, 0xc6, 0xa8, 0xb7, 0xf7, 0x73, 0x7f, 0x4b, + 0x59, 0x7e, 0x30, 0x51, 0x99, 0xc8, 0xde, 0x6f, 0x73, 0xf3, 0xce, 0xb7, 0x30, 0xcb, 0xf6, 0xe8, + 0x7e, 0x53, 0x11, 0x4d, 0x93, 0x10, 0x24, 0xdc, 0xc1, 0xf3, 0xef, 0xc5, 0x6b, 0xac, 0x8b, 0xcb, + 0x37, 0x4c, 0x92, 0xe2, 0xd6, 0x87, 0xe7, 0x35, 0xda, 0x89, 0x27, 0x26, 0xb2, 0xe7, 0xb3, 0x01, + 0x35, 0xd1, 0xcc, 0xdf, 0xfa, 0x0f, 0x81, 0xb8, 0x98, 0x6a, 0x9e, 0x64, 0x6b, 0x9d, 0x87, 0x54, + 0x87, 0xd9, 0x60, 0xfe, 0x87, 0x36, 0x0c, 0x62, 0x55, 0x57, 0xcd, 0x5e, 0xd7, 0xfd, 0x45, 0xd1, + 0x8c, 0xfd, 0xa9, 0x7b, 0x41, 0x0c, 0xa6, 0x2b, 0xae, 0xee, 0x3c, 0x90, 0xc9, 0xec, 0x02, 0x52, + 0x5d, 0x74, 0x60, 0x6e, 0xc7, 0xec, 0x11, 0x69, 0xb2, 0x50, 0xe8, 0xff, 0x4b, 0x94, 0xde, 0x08, + 0xaa, 0x95, 0x15, 0x0e, 0x52, 0x71, 0x5c, 0x18, 0xdd, 0x6a, 0x03, 0xf2, 0xa3, 0x99, 0x95, 0x6f, + 0x33, 0x08, 0xb8, 0xce, 0x72, 0xa9, 0x6d, 0x2f, 0x41, 0xe2, 0xcd, 0x90, 0x1d, 0x4d, 0x50, 0x7f, + 0x33, 0x63, 0x54, 0xac, 0x24, 0xda, 0x68, 0xcf, 0x8f, 0x83, 0xb6, 0x9f, 0x12, 0xe5, 0x4b, 0xac, + 0x40, 0xf9, 0xa7, 0xbc, 0xb1, 0xd1, 0x16, 0x5e, 0x45, 0x6d, 0xde, 0x5c, 0x60, 0xf5, 0xbd, 0xa4, + 0xf8, 0xc4, 0x35, 0x55, 0x0b, 0x49, 0x2b, 0x14, 0x64, 0x2a, 0x32, 0x9c, 0x90, 0x73, 0x2e, 0x66, + 0x18, 0xcc, 0x73, 0xb5, 0xa3, 0x2c, 0x71, 0xcf, 0x30, 0x6b, 0xd8, 0x9b, 0x07, 0x4c, 0x81, 0x74, + 0x91, 0x25, 0x56, 0xc3, 0xfb, 0x05, 0xdb, 0xd4, 0x21, 0x6b, 0x24, 0x62, 0x39, 0xb0, 0x96, 0xcb, + 0x89, 0x28, 0x16, 0x2e, 0x35, 0x44, 0xfc, 0x40, 0xea, 0xf6, 0x52, 0xcc, 0x91, 0x00, 0x46, 0x86, + 0xfd, 0xe0, 0xa3, 0xfd, 0xfd, 0x80, 0xbf, 0x27, 0x70, 0xdd, 0x4f, 0xb7, 0x9a, 0x70, 0x29, 0xb0, + 0xda, 0x6b, 0x01, 0xc8, 0xdb, 0x06, 0x94, 0x14, 0xb2, 0x6a, 0x05, 0xc1, 0x80, 0xcc, 0xdf, 0xfe, + 0x04, 0x94, 0x01, 0xa3, 0x48, 0x99, 0x0e, 0x02, 0x47, 0xb5, 0x42, 0xc7, 0xcf, 0xcd, 0xe0, 0x12, + 0xf3, 0xd6, 0x84, 0x0f, 0x40, 0x6f, 0x7b, 0xe7, 0x82, 0xdb, 0xcf, 0x4f, 0x60, 0x80, 0xba, 0xfc, + 0x7b, 0xbb, 0x20, 0x1e, 0xf6, 0x08, 0x7d, 0x38, 0xc2, 0x11, 0x72, 0x7c, 0x15, 0xec, 0xa3, 0x0f, + 0x00, 0x3a, 0xd5, 0x2c, 0xd5, 0x40, 0xd8, 0xbc, 0x4d, 0xb8, 0x13, 0x75, 0xf4, 0x4e, 0x0e, 0x09, + 0x2a, 0x06, 0xf9, 0xe9, 0x89, 0x1f, 0x6a, 0xaa, 0x17, 0x8f, 0xc3, 0x9b, 0x3e, 0x28, 0x68, 0x28, + 0x43, 0x30, 0xb3, 0xd2, 0x50, 0x8b, 0x48, 0x68, 0x36, 0xd9, 0xf1, 0xf5, 0xaf, 0x04, 0x64, 0xae, + 0x1d, 0xd2, 0xd6, 0x96, 0xac, 0x0e, 0xfa, 0xa4, 0xd9, 0xde, 0xf2, 0xe5, 0xcd, 0x28, 0xa5, 0xe1, + 0xd4, 0x2e, 0x81, 0x3d, 0x3f, 0x16, 0xdb, 0x85, 0xbf, 0x56, 0x5e, 0xde, 0xae, 0x86, 0x9a, 0x88, + 0xd0, 0x82, 0xef, 0x43, 0xa3, 0x3f, 0x93, 0x65, 0x33, 0xd1, 0x51, 0xe0, 0xbd, 0x06, 0x45, 0x71, + 0x35, 0xfa, 0x04, 0x50, 0xaa, 0xc6, 0x54, 0x38, 0xc7, 0x29, 0xaa, 0xc8, 0x59, 0x83, 0x99, 0xfe, + 0x8a, 0xc0, 0x04, 0x2e, 0x32, 0x20, 0xe9, 0x36, 0xf8, 0x82, 0x95, 0xe4, 0x5b, 0xaf, 0x65, 0x3f, + 0x40, 0x5b, 0x12, 0xb2, 0xfa, 0x9d, 0x38, 0x6b, 0xbc, 0xe3, 0xb4, 0x61, 0x37, 0x25, 0x3e, 0x7c, + 0x64, 0xbc, 0xe7, 0xe9, 0x98, 0xdc, 0xb4, 0x91, 0x63, 0x90, 0xff, 0x7e, 0x2f, 0x07, 0x4f, 0xb1, + 0x75, 0x4b, 0x60, 0xc3, 0x41, 0x8d, 0xc6, 0xc5, 0x72, 0x8e, 0x2f, 0xa3, 0x64, 0xac, 0xb2, 0xc8, + 0xbf, 0x63, 0x52, 0x58, 0x49, 0xf2, 0xce, 0xc2, 0x68, 0xda, 0xb2, 0x4c, 0xa7, 0x3c, 0x42, 0x9e, + 0xc3, 0x50, 0xda, 0x29, 0x00, 0x58, 0x3d, 0x9c, 0x8b, 0x2b, 0xd0, 0xaa, 0x7f, 0x54, 0x58, 0xb0, + 0x0d, 0xe9, 0xe4, 0x42, 0x09, 0x94, 0x6f, 0x70, 0xbe, 0xf1, 0xc7, 0xec, 0xa2, 0x6c, 0xf0, 0x8d, + 0xef, 0x56, 0xcd, 0xbd, 0x6c, 0x18, 0x16, 0xd5, 0x2a, 0x37, 0x3f, 0x82, 0x23, 0x21, 0x46, 0x87, + 0x70, 0xa6, 0xc5, 0xd8, 0xbe, 0x13, 0x0e, 0x6c, 0x75, 0x2d, 0x63, 0xa7, 0x26, 0x88, 0xb7, 0x86, + 0x16, 0x84, 0x80, 0xaf, 0x50, 0x5d, 0x91, 0x4c, 0x13, 0xea, 0xe5, 0x79, 0x05, 0xae, 0x47, 0xe1, + 0x0c, 0xfb, 0xd6, 0x2c, 0x20, 0x97, 0xa7, 0x48, 0x24, 0x32, 0xcf, 0xe5, 0x50, 0x0b, 0x80, 0x4e, + 0x37, 0x15, 0x41, 0x1a, 0xab, 0x78, 0x7f, 0xdc, 0xee, 0x2f, 0x64, 0x4f, 0x38, 0xee, 0x9a, 0x07, + 0x84, 0xe8, 0x8d, 0xec, 0x94, 0xfb, 0x95, 0x99, 0xa6, 0x8a, 0x01, 0x05, 0x57, 0x71, 0x62, 0xa6, + 0xfd, 0x57, 0x5e, 0xdb, 0xfa, 0xb3, 0xd6, 0xda, 0x5d, 0x2c, 0xa2, 0xf0, 0x86, 0xa8, 0xd5, 0x5a, + 0x32, 0x5f, 0x8e, 0xbd, 0xa8, 0x4e, 0x79, 0xd9, 0x6d, 0x79, 0x40, 0x97, 0xe6, 0x24, 0x13, 0xca, }; -static const uint8_t ml_dsa_65_0_sig_msg[] = { - 0xe3, 0xd5, 0x4d, 0xba, 0x67, 0x5d, 0xe7, 0x53, 0x0d, 0x38, 0x54, 0xc8, 0xcd, 0xbd, 0x58, 0x1c, - 0x6e, 0x39, 0x2f, 0x69, 0xcf, 0x97, 0x93, 0xd8, 0xc0, 0xbb, 0xaf, 0xee, 0x73, 0x34, 0xc5, 0x87, - 0x8b, 0xf1, 0x3b, 0x3b, 0xdc, 0xf1, 0xd9, 0x93, 0xa4, 0x7a, 0x7e, 0x8e, 0xdd, 0x4b, 0x6c, 0x3e, - 0xc9, 0x15, 0xa5, 0xe8, 0xfb, 0xf2, 0x07, 0x1f, 0x11, 0x2a, 0xac, 0x41, 0xf0, 0x8c, 0xcd, 0x8c, - 0xc7, 0x31, 0x14, 0x3a, 0xfc, 0x22, 0x32, 0x13, 0x21, 0x2a, 0x5b, 0xd6, 0xb6, 0xa4, 0x02, 0x6c, - 0xfc, 0x9f, 0x7a, 0x41, 0xc0, 0x8c, 0xb9, 0x9f, 0x2b, 0x37, 0x5d, 0x0e, 0x04, 0xfb, 0xbe, 0x00, - 0x50, 0x35, 0x33, 0xdf, 0xb8, 0x69, 0xde, 0x43, 0x56, 0x65, 0x74, 0xb4, 0x92, 0x32, 0x56, 0x6e, - 0x38, 0xb1, 0x3c, 0xaf, 0x8c, 0x1e, 0xc1, 0xfa, 0x11, 0xcc, 0xa3, 0xdb, 0x66, 0x88, 0x79, 0x96, - 0xdf, 0xee, 0x09, 0x0d, 0x13, 0x3b, 0x1d, 0x39, 0x4f, 0x07, 0x5f, 0x82, 0x86, 0xca, 0x6c, 0xce, - 0x13, 0xc3, 0x97, 0x7d, 0x92, 0x9d, 0xef, 0x59, 0x43, 0x46, 0x1b, 0xd5, 0xa2, 0x2d, 0x4b, 0xb5, - 0xe8, 0xfb, 0x7e, 0xfe, 0x01, 0x69, 0x32, 0xd0, 0x2e, 0xcf, 0x4d, 0x7b, 0x77, 0x05, 0x41, 0x2c, - 0xa1, 0x45, 0x08, 0xb1, 0x3e, 0x61, 0x92, 0x5e, 0xec, 0x0b, 0x77, 0x46, 0x0c, 0x4c, 0x08, 0x9c, - 0x83, 0xd6, 0x87, 0xc8, 0xf7, 0x0d, 0x14, 0x1c, 0x75, 0xd6, 0xdf, 0x24, 0xfc, 0xdb, 0x64, 0xbb, - 0x8c, 0x27, 0x21, 0xaa, 0xc9, 0xce, 0x8c, 0x6f, 0xc4, 0x75, 0x0c, 0x16, 0x86, 0xa7, 0xa7, 0x0d, - 0xf3, 0x49, 0x57, 0xdc, 0xc0, 0xce, 0x53, 0xc3, 0xf0, 0xd2, 0x80, 0xcb, 0xd0, 0x56, 0xd4, 0xf9, - 0x21, 0xfd, 0xed, 0xb3, 0x5a, 0xfc, 0xe5, 0xdb, 0xb3, 0x48, 0x96, 0x4a, 0xd1, 0x05, 0xee, 0xf5, - 0xa1, 0xb0, 0x7b, 0xd0, 0xd5, 0x86, 0xb8, 0x7a, 0xda, 0x7c, 0xa7, 0xaf, 0xa9, 0x3f, 0xb7, 0x5a, - 0xc0, 0xde, 0x1c, 0x74, 0x9c, 0x48, 0xbd, 0x6f, 0x19, 0x2d, 0x62, 0x79, 0x48, 0x2d, 0x3f, 0x97, - 0x81, 0x29, 0x0b, 0x88, 0xb0, 0x95, 0xd7, 0x3e, 0x72, 0x8a, 0xfc, 0x18, 0x6b, 0xff, 0x08, 0x16, - 0xc1, 0xde, 0x78, 0x11, 0x9d, 0x70, 0x1f, 0xad, 0x38, 0x2c, 0x65, 0xac, 0xc4, 0x2b, 0x04, 0xd6, - 0x94, 0x12, 0xae, 0xb2, 0x55, 0x79, 0x38, 0x39, 0xef, 0xb9, 0xe5, 0xbb, 0xf6, 0xf6, 0x9b, 0x7f, - 0xda, 0x27, 0xa4, 0x4c, 0x04, 0xd3, 0xaa, 0x2f, 0xdc, 0x61, 0x01, 0xe9, 0xce, 0xc6, 0xf7, 0x57, - 0x45, 0xf2, 0x0e, 0x07, 0x7e, 0xe2, 0x3d, 0x65, 0x2b, 0x7d, 0xe5, 0x47, 0xa0, 0xfb, 0x0e, 0x3a, - 0x15, 0x34, 0x73, 0xbc, 0x82, 0x4a, 0xc8, 0xa3, 0x1a, 0x2c, 0x94, 0x93, 0xde, 0x64, 0xb6, 0xd4, - 0x01, 0x85, 0xb6, 0x04, 0x31, 0xb1, 0xb3, 0x4e, 0xe3, 0x27, 0x97, 0xcc, 0x77, 0xc4, 0x5c, 0x71, - 0xf4, 0xe0, 0x4e, 0x84, 0x9b, 0xf8, 0xdd, 0xfc, 0xf6, 0x40, 0xb8, 0x31, 0x30, 0x20, 0x8d, 0xc2, - 0x49, 0x5c, 0x0c, 0xfc, 0x6c, 0x03, 0x1b, 0x63, 0xb8, 0x0f, 0xb4, 0x5f, 0x47, 0xbf, 0xe3, 0xd2, - 0x5e, 0xc5, 0xb3, 0x6c, 0x27, 0x00, 0x30, 0x0d, 0x82, 0x23, 0x07, 0x4c, 0x15, 0x6c, 0x5d, 0x98, - 0x87, 0xde, 0x8c, 0x5a, 0x97, 0xba, 0x36, 0xa8, 0x4d, 0x75, 0x85, 0xcc, 0x06, 0x5f, 0x20, 0xfe, - 0x8d, 0xce, 0x9e, 0x8e, 0xb8, 0x14, 0xa1, 0xd5, 0xb7, 0xa3, 0xb7, 0xa3, 0xab, 0x68, 0xd9, 0x3e, - 0xdb, 0xbb, 0xbf, 0x67, 0xbd, 0x70, 0xa0, 0xfc, 0x53, 0x6c, 0x81, 0xc8, 0xd6, 0xe8, 0xb1, 0x39, - 0xc6, 0xb3, 0xb5, 0xee, 0xaa, 0x39, 0x53, 0x46, 0x45, 0x49, 0xcb, 0x38, 0xb7, 0x8a, 0x15, 0xce, - 0x3d, 0x5b, 0xb4, 0xb7, 0x98, 0xe6, 0xb1, 0x42, 0xbc, 0x48, 0xc7, 0x0c, 0x5b, 0xe6, 0x1f, 0x82, - 0xdb, 0x6e, 0x2e, 0xc0, 0x36, 0x16, 0x99, 0x90, 0x73, 0x57, 0x4e, 0x6f, 0xb3, 0x5d, 0x44, 0x1b, - 0x78, 0xdc, 0xaf, 0xf5, 0x6b, 0x8c, 0xa8, 0xf4, 0x73, 0xec, 0x05, 0x43, 0xc9, 0xc6, 0x9e, 0x93, - 0xb9, 0x7f, 0x95, 0xfc, 0xa5, 0x8d, 0xaa, 0xed, 0x51, 0x1f, 0x44, 0x40, 0xdb, 0x25, 0x2e, 0xc1, - 0x8c, 0x90, 0xd2, 0xf9, 0x7a, 0xd9, 0x01, 0x71, 0x6a, 0xd3, 0x4e, 0xe1, 0x03, 0xea, 0xb0, 0xb7, - 0x98, 0x29, 0x94, 0xe2, 0x1e, 0xd1, 0xac, 0x4b, 0xd6, 0x2a, 0xce, 0x6d, 0xfe, 0x12, 0xc8, 0xc2, - 0xcd, 0x09, 0x2e, 0x7a, 0x7e, 0x37, 0xbd, 0x4e, 0x61, 0xb4, 0xcd, 0x51, 0xd9, 0x80, 0x17, 0x1f, - 0x8d, 0x42, 0x53, 0xa4, 0xe7, 0xfc, 0xca, 0xba, 0x0a, 0x51, 0x30, 0x38, 0x27, 0x63, 0x25, 0x37, - 0x25, 0x27, 0x62, 0x1b, 0x0a, 0x3d, 0xfa, 0xec, 0xcd, 0x7c, 0xdf, 0xe9, 0x8c, 0xb7, 0x9c, 0x06, - 0x62, 0xf5, 0xd0, 0x23, 0x4a, 0xee, 0xe9, 0x30, 0x24, 0x38, 0xb7, 0x78, 0x23, 0x8b, 0xca, 0x40, - 0x9d, 0x59, 0x08, 0x76, 0x87, 0x79, 0x15, 0x5d, 0x65, 0x9e, 0x92, 0xd1, 0x2c, 0xee, 0xf3, 0xc2, - 0xc9, 0x54, 0xc3, 0xf1, 0xa4, 0xc2, 0x91, 0x2e, 0x9e, 0xe9, 0x94, 0x20, 0x9b, 0x60, 0x8a, 0x60, - 0x4d, 0x0c, 0xd8, 0x7f, 0x79, 0x66, 0x5a, 0x25, 0xe7, 0x74, 0xe6, 0xdc, 0x1d, 0x88, 0x19, 0xb8, - 0x58, 0x52, 0xd9, 0xad, 0xeb, 0x17, 0x4c, 0x75, 0xd9, 0x82, 0xdc, 0xe2, 0x9f, 0xfd, 0xd2, 0x4b, - 0x7a, 0xaf, 0x59, 0x46, 0xa7, 0x4c, 0x14, 0xa3, 0x01, 0xae, 0x09, 0xf1, 0x38, 0x6a, 0xb1, 0x7a, - 0xcc, 0x34, 0x1d, 0x6d, 0xac, 0x04, 0x0f, 0xa6, 0x52, 0x7f, 0xcb, 0x8e, 0x09, 0x5c, 0xd4, 0xf1, - 0x65, 0x1e, 0x47, 0xce, 0x02, 0xe6, 0xed, 0x76, 0xc0, 0x85, 0xb2, 0xfa, 0xbe, 0x9f, 0xf5, 0xa5, - 0x10, 0xe6, 0x3e, 0x73, 0xee, 0x38, 0x6a, 0xc1, 0x7d, 0x5c, 0x2c, 0x94, 0xc2, 0xd5, 0x28, 0xb3, - 0xa9, 0xc8, 0x90, 0x2d, 0x10, 0xc1, 0x61, 0x0b, 0xa7, 0x0b, 0xa6, 0x16, 0xc5, 0x8d, 0x03, 0x7f, - 0x14, 0x8e, 0x63, 0x32, 0x10, 0x7d, 0xb9, 0xf6, 0x4c, 0x57, 0x90, 0x64, 0xf1, 0x94, 0x81, 0x66, - 0x9c, 0x78, 0x88, 0xc2, 0x1f, 0x0a, 0x33, 0x74, 0x19, 0x4f, 0xf1, 0xa7, 0x2f, 0xb6, 0x18, 0x0d, - 0xc0, 0x1d, 0x29, 0x44, 0x3b, 0xc5, 0x2c, 0x31, 0x8a, 0xb0, 0x38, 0x43, 0x35, 0x7a, 0x5a, 0x98, - 0xb3, 0xd5, 0xcd, 0xde, 0x9b, 0xc4, 0x9d, 0x3e, 0xd2, 0x9e, 0xf7, 0x3c, 0xaf, 0x44, 0xa7, 0x84, - 0x9a, 0xc6, 0xdf, 0x4b, 0x2d, 0x35, 0x3b, 0x6a, 0x56, 0x23, 0x86, 0xbd, 0x48, 0x05, 0x12, 0x10, - 0x8c, 0xa0, 0xec, 0xd6, 0x3b, 0xb3, 0xd3, 0xa0, 0x69, 0x56, 0x79, 0x76, 0x2f, 0x82, 0x54, 0x11, - 0x6d, 0x80, 0x49, 0x48, 0x8b, 0xe6, 0x38, 0x9f, 0xac, 0x08, 0x91, 0x6e, 0x64, 0x07, 0x94, 0x4b, - 0x15, 0x26, 0xbf, 0x03, 0x55, 0x15, 0x04, 0xe4, 0x0e, 0x6a, 0x32, 0x66, 0xd4, 0xe7, 0x73, 0xe8, - 0x92, 0xea, 0xc4, 0xdd, 0x51, 0x28, 0x37, 0x4b, 0xa3, 0x41, 0xeb, 0x40, 0x97, 0xe2, 0x0a, 0x9b, - 0xbd, 0x4e, 0x5e, 0xa2, 0x6b, 0xe6, 0xfc, 0x32, 0x5d, 0x9b, 0x71, 0xc2, 0x32, 0xdc, 0xe1, 0x97, - 0x49, 0x28, 0x51, 0x6e, 0xa2, 0x51, 0x99, 0x74, 0xc8, 0x2a, 0xea, 0x52, 0x47, 0x62, 0x7f, 0x80, - 0x2c, 0x64, 0x42, 0xb6, 0x7f, 0xbc, 0x10, 0xe3, 0x59, 0x67, 0x2e, 0xeb, 0x9d, 0xff, 0x76, 0x8e, - 0x93, 0x33, 0xa2, 0x16, 0x63, 0x36, 0xff, 0x00, 0x04, 0xde, 0x5e, 0x7b, 0xca, 0x55, 0x99, 0x8c, - 0x86, 0x0f, 0xc1, 0xa8, 0x6b, 0x75, 0xcb, 0xeb, 0x3f, 0xb5, 0xa5, 0xa2, 0x51, 0x9d, 0x52, 0x27, - 0xa6, 0xb5, 0xa7, 0xe0, 0xee, 0x05, 0x8f, 0xee, 0x0c, 0xae, 0x2b, 0x4a, 0xa7, 0x50, 0x99, 0xff, - 0x09, 0x80, 0x89, 0xf0, 0x26, 0x1c, 0x44, 0x3b, 0x13, 0x91, 0x1c, 0xf3, 0x25, 0x0c, 0xae, 0x2f, - 0x29, 0x06, 0xcb, 0x64, 0x54, 0xa2, 0x0d, 0x0f, 0xff, 0xe8, 0xc5, 0xaf, 0xb1, 0xde, 0x7c, 0x5b, - 0xa8, 0xca, 0xae, 0x00, 0xcd, 0x7c, 0x2d, 0x72, 0x19, 0xfc, 0x83, 0xfb, 0x15, 0x8f, 0xd6, 0xd1, - 0x5e, 0xdc, 0x29, 0x24, 0xab, 0x41, 0xc9, 0xb8, 0x81, 0x56, 0x6c, 0x53, 0x8f, 0x5e, 0x38, 0x97, - 0x8c, 0x31, 0x82, 0xfb, 0xdd, 0x0c, 0x0c, 0x53, 0x2a, 0x77, 0xa6, 0x1e, 0x5b, 0x8e, 0x66, 0x6b, - 0x3c, 0x54, 0x0d, 0xe9, 0x76, 0x09, 0x8d, 0x2b, 0xc6, 0x8c, 0x0c, 0xda, 0xcf, 0x57, 0xe9, 0xbf, - 0x1b, 0xde, 0x8a, 0x7b, 0xfb, 0x13, 0x25, 0x6b, 0xc6, 0x9f, 0x2e, 0x38, 0x6d, 0x8f, 0x45, 0x8d, - 0x30, 0x3b, 0xab, 0x78, 0x1e, 0x53, 0x75, 0x17, 0xad, 0x7d, 0x0f, 0x03, 0x78, 0xd2, 0xf2, 0x58, - 0xb0, 0x13, 0xae, 0xa0, 0x1c, 0x97, 0xa7, 0xe8, 0xcd, 0x2c, 0x3d, 0xeb, 0x20, 0x3a, 0xa8, 0xd5, - 0x90, 0x84, 0xac, 0xc2, 0xb0, 0x9a, 0xd3, 0xc9, 0x7e, 0xa6, 0x13, 0xa2, 0xfb, 0xf2, 0x97, 0x03, - 0x8b, 0x4e, 0x72, 0x16, 0x65, 0xd5, 0x12, 0xdd, 0xfe, 0x5c, 0x13, 0xf1, 0x1d, 0xf4, 0xbd, 0xc0, - 0x20, 0x0b, 0xf6, 0x10, 0xd5, 0x7e, 0xa7, 0x23, 0x29, 0x14, 0x75, 0x2f, 0x75, 0xa5, 0x44, 0xe0, - 0x1d, 0x5a, 0xe0, 0xde, 0xfb, 0xc9, 0xe1, 0x86, 0x95, 0x82, 0x40, 0x58, 0xbd, 0xfc, 0xdd, 0xf4, - 0xd5, 0x88, 0x48, 0x40, 0xd2, 0xb5, 0x5d, 0x6c, 0x2d, 0x6e, 0xdf, 0x47, 0x5c, 0xa7, 0x5f, 0x3e, - 0xb4, 0xd0, 0xed, 0xfa, 0xe9, 0x93, 0x49, 0x68, 0x4b, 0xc9, 0xc5, 0x33, 0xea, 0xd5, 0x91, 0x08, - 0x5b, 0xd1, 0xd6, 0xd4, 0xe4, 0xf9, 0xb0, 0x0a, 0x54, 0xc9, 0x72, 0x44, 0x32, 0x2d, 0x50, 0x5a, - 0xb5, 0x2b, 0x2d, 0x4e, 0x04, 0x38, 0xc6, 0xac, 0x5d, 0x2c, 0x39, 0x83, 0xbf, 0xb2, 0x35, 0x9f, - 0xb2, 0x11, 0x74, 0xac, 0x02, 0x15, 0x6a, 0xe1, 0x1e, 0xf2, 0x33, 0xaf, 0x40, 0xa6, 0x95, 0x85, - 0x4c, 0x84, 0xe6, 0x20, 0x75, 0x44, 0x19, 0xcf, 0x7e, 0xd9, 0xea, 0x4f, 0xda, 0xce, 0xfc, 0x88, - 0x79, 0xb2, 0xeb, 0xc7, 0xb2, 0x9c, 0xdf, 0x9c, 0x4c, 0xa2, 0x92, 0x1e, 0x08, 0x44, 0x86, 0x47, - 0x7c, 0x14, 0x52, 0xbc, 0x3b, 0xaf, 0x52, 0x9d, 0xd9, 0x27, 0x48, 0xe2, 0xb1, 0xb6, 0x84, 0xb1, - 0x10, 0x3c, 0xf4, 0xc6, 0xee, 0x11, 0x99, 0x9e, 0x41, 0x21, 0xcf, 0xc7, 0xe2, 0xf3, 0xf6, 0xc7, - 0xbb, 0x9c, 0x05, 0x5f, 0x80, 0x0d, 0xc4, 0x6d, 0x78, 0xd9, 0xd9, 0x4c, 0x3b, 0x20, 0xbd, 0x1b, - 0x69, 0x74, 0x11, 0xff, 0x0b, 0x41, 0xe3, 0x81, 0x73, 0xd2, 0xc9, 0xff, 0x1c, 0xca, 0x77, 0xb2, - 0x73, 0x84, 0x02, 0x6e, 0xd2, 0xee, 0x25, 0x22, 0x3b, 0x00, 0x07, 0xd2, 0x93, 0x0d, 0x70, 0x0a, - 0x20, 0xec, 0x91, 0x59, 0xa6, 0x6a, 0x4b, 0xbe, 0x0b, 0xd8, 0x4d, 0xa5, 0x2f, 0x00, 0x6d, 0x7f, - 0xac, 0x6d, 0x59, 0xab, 0x2b, 0xde, 0x08, 0xfb, 0xce, 0xb7, 0x6a, 0x57, 0xff, 0xef, 0x59, 0xc0, - 0x77, 0xf9, 0x00, 0x8a, 0x4a, 0x23, 0x1f, 0xcc, 0x30, 0x2e, 0x4d, 0xda, 0xe9, 0xf3, 0xda, 0xa5, - 0x57, 0x07, 0xc1, 0xed, 0xcd, 0xe8, 0x04, 0x21, 0x76, 0xdc, 0x7d, 0xc6, 0x37, 0x7b, 0xec, 0x58, - 0xb2, 0x36, 0x99, 0x0a, 0xa6, 0xc5, 0x87, 0x6d, 0x5a, 0x27, 0x94, 0x2f, 0xff, 0x7f, 0xd7, 0x4b, - 0x7d, 0xa1, 0xd2, 0xec, 0x47, 0x0c, 0xf7, 0x11, 0x03, 0xa6, 0x4f, 0xd2, 0x91, 0x7a, 0xf4, 0xc4, - 0x5d, 0x4c, 0x59, 0xc1, 0x3f, 0x58, 0x47, 0x2a, 0xf4, 0x08, 0xf9, 0xc5, 0xf9, 0x07, 0xc7, 0xc6, - 0xae, 0x0f, 0x40, 0x2b, 0x40, 0x4a, 0xa4, 0x0c, 0xf8, 0xd9, 0x62, 0xed, 0xef, 0x7b, 0x76, 0x54, - 0xec, 0x8e, 0x36, 0xc6, 0x45, 0x85, 0x5b, 0xbe, 0xcc, 0x13, 0x37, 0xec, 0x18, 0xf0, 0xea, 0x65, - 0x93, 0x3f, 0x4d, 0x07, 0xce, 0xe1, 0x52, 0x94, 0x13, 0x9f, 0x47, 0x9a, 0x16, 0x13, 0x3d, 0x2b, - 0xf4, 0x12, 0xb5, 0xe4, 0x6d, 0x5b, 0x06, 0xfb, 0x17, 0x0f, 0x0c, 0x93, 0xdf, 0xb8, 0xac, 0x3f, - 0x74, 0x78, 0x00, 0x2c, 0xa7, 0x6b, 0x67, 0xa2, 0x72, 0xa3, 0x22, 0x4c, 0x81, 0x72, 0xcd, 0xd8, - 0xa6, 0x16, 0xa4, 0xc5, 0x2f, 0x5f, 0xde, 0x94, 0x6c, 0xf5, 0x73, 0x35, 0x83, 0xc5, 0x38, 0x35, - 0xeb, 0xc9, 0x0d, 0xe3, 0xec, 0xe3, 0x42, 0x99, 0xd7, 0xe5, 0x43, 0x4b, 0xf0, 0x72, 0xf0, 0x8a, - 0x44, 0x90, 0x58, 0x18, 0xee, 0x97, 0x08, 0x81, 0x5c, 0xed, 0x10, 0xe9, 0x04, 0x3e, 0x88, 0xb6, - 0xef, 0x12, 0xbe, 0x72, 0xfc, 0x11, 0x69, 0xff, 0x2f, 0x92, 0x5c, 0x38, 0xe0, 0x3b, 0x82, 0x7c, - 0xde, 0x77, 0x74, 0x0c, 0x7c, 0x28, 0x48, 0xaa, 0x3b, 0x09, 0xf5, 0x55, 0xe2, 0x71, 0x2b, 0x34, - 0x1e, 0x24, 0x87, 0x72, 0xef, 0xcf, 0x01, 0xb9, 0x9c, 0xd5, 0x40, 0x36, 0x42, 0x44, 0x8b, 0x52, - 0x43, 0xf3, 0x5a, 0x30, 0x73, 0xa6, 0xda, 0x76, 0x85, 0xad, 0x42, 0xfe, 0xcb, 0x05, 0xd4, 0x6f, - 0xc9, 0x9f, 0x7d, 0x6b, 0x8e, 0xc5, 0x6f, 0x39, 0xa7, 0xe7, 0xb3, 0x57, 0x9f, 0xcd, 0x58, 0x7e, - 0x07, 0xc0, 0x5b, 0xb7, 0xe1, 0xf5, 0xd4, 0x9b, 0x90, 0x15, 0x37, 0x5e, 0x09, 0xa9, 0xa1, 0x4f, - 0x2c, 0xdf, 0x04, 0x9a, 0xc3, 0x92, 0x13, 0x9e, 0xaa, 0x3c, 0x25, 0x47, 0x42, 0x6b, 0x8c, 0xbc, - 0x0c, 0x4b, 0xa7, 0x6c, 0x55, 0x01, 0x4f, 0xcd, 0x39, 0x40, 0xfe, 0xa2, 0x63, 0x1a, 0x75, 0xcd, - 0x2f, 0xf5, 0xc2, 0x2b, 0x8a, 0x65, 0xc9, 0xaf, 0x2d, 0xb1, 0xb8, 0x31, 0xc3, 0x0e, 0xdc, 0x8e, - 0xb7, 0x9b, 0x79, 0x7e, 0x76, 0xd0, 0x45, 0xef, 0x0b, 0x8e, 0xff, 0x8d, 0x41, 0x40, 0x4f, 0x62, - 0x6d, 0xe8, 0x55, 0x1f, 0xfa, 0x44, 0x58, 0x85, 0xca, 0x1e, 0x3c, 0x9b, 0xf0, 0x5b, 0x5c, 0x39, - 0xcd, 0x40, 0xe3, 0xb3, 0x7c, 0xe3, 0xb5, 0x85, 0x14, 0xbc, 0x70, 0xe2, 0xea, 0x94, 0xcd, 0x69, - 0x4c, 0xa8, 0x8c, 0x1f, 0x47, 0xaa, 0xcf, 0x9b, 0x4f, 0xf6, 0x62, 0x2c, 0x55, 0x5a, 0xab, 0x7c, - 0x71, 0xea, 0x21, 0x0f, 0xaa, 0x9c, 0x0b, 0xf9, 0x08, 0x9d, 0xc9, 0xc9, 0xdf, 0xa7, 0x7c, 0x2c, - 0x4a, 0x92, 0xf5, 0x9b, 0xd7, 0xcd, 0x24, 0xef, 0x4f, 0x8d, 0x50, 0x88, 0xdd, 0xbe, 0x22, 0xde, - 0x85, 0x4f, 0x89, 0x66, 0xc0, 0x17, 0xfb, 0x41, 0x44, 0x64, 0xed, 0x6b, 0x99, 0x1e, 0x28, 0x9f, - 0x7e, 0x3f, 0xea, 0x78, 0x38, 0x31, 0xcc, 0xfb, 0x51, 0xe2, 0x27, 0xa6, 0xdd, 0x4f, 0x5e, 0xf5, - 0x09, 0x63, 0xe8, 0x08, 0x47, 0xfa, 0x6b, 0x35, 0x0a, 0x2d, 0xcd, 0xa3, 0xc8, 0x75, 0xd1, 0x76, - 0x34, 0x2e, 0xd7, 0x3b, 0xb4, 0x2b, 0x41, 0x6e, 0x58, 0xd1, 0x61, 0x2c, 0x4d, 0x06, 0x6f, 0x12, - 0x07, 0xb6, 0xe1, 0x6a, 0x95, 0x5d, 0xcd, 0x55, 0x7a, 0x9e, 0xa3, 0x7a, 0x9f, 0x24, 0xcc, 0xb2, - 0x0e, 0x74, 0xac, 0x4d, 0x61, 0x66, 0xac, 0xeb, 0xcd, 0x7d, 0x65, 0x14, 0x50, 0xe5, 0xd4, 0xae, - 0xa8, 0x6d, 0xc8, 0x95, 0x28, 0xdf, 0xeb, 0xe0, 0x7b, 0x39, 0xa1, 0x60, 0x16, 0x0f, 0xeb, 0xbe, - 0xe5, 0xb1, 0x11, 0x22, 0xb4, 0x17, 0x7f, 0xd0, 0x4b, 0x0a, 0x49, 0x80, 0x3e, 0x49, 0x8f, 0x1b, - 0xff, 0xba, 0x89, 0x8b, 0xe3, 0xb2, 0x63, 0x20, 0x96, 0x61, 0x65, 0xe1, 0x36, 0xf7, 0xf5, 0x17, - 0xfe, 0xa3, 0x5e, 0xdf, 0x01, 0xd3, 0x68, 0x31, 0xf9, 0xab, 0x47, 0x8c, 0xff, 0x82, 0x41, 0xe0, - 0x1a, 0xd7, 0x57, 0x0c, 0x26, 0xea, 0x62, 0x43, 0x90, 0x13, 0xf6, 0x1d, 0x48, 0x79, 0x7b, 0x92, - 0x96, 0x49, 0x03, 0x80, 0x2a, 0xaa, 0xf3, 0x56, 0xc6, 0xaf, 0x76, 0x45, 0xf5, 0xf4, 0x33, 0x8c, - 0x7e, 0xad, 0x5a, 0xb2, 0x89, 0x8c, 0x60, 0x01, 0x23, 0x88, 0x8a, 0xf4, 0x2a, 0x2f, 0xe0, 0x6f, - 0xf0, 0x3d, 0x81, 0x32, 0x0f, 0xc0, 0x8e, 0x75, 0x89, 0xe1, 0x98, 0x87, 0xec, 0x86, 0xcc, 0x1b, - 0x08, 0x20, 0x4c, 0x29, 0xe8, 0x6c, 0xcf, 0x18, 0x7b, 0x12, 0x40, 0xb8, 0x1f, 0x7d, 0x37, 0x5f, - 0x37, 0x62, 0xcb, 0xc1, 0x0f, 0x3b, 0xad, 0xca, 0xd7, 0x43, 0x1e, 0x59, 0xa4, 0xdc, 0x13, 0x64, - 0x40, 0xaa, 0x72, 0x62, 0x8e, 0x17, 0xd2, 0xbc, 0xff, 0x91, 0x6d, 0xbc, 0x8a, 0x66, 0xb8, 0xa8, - 0x3a, 0x3c, 0x64, 0x00, 0xea, 0x0c, 0xc5, 0x7a, 0x82, 0xdb, 0x41, 0xbb, 0x28, 0xce, 0xa9, 0xe9, - 0xce, 0xca, 0x1e, 0xd8, 0x26, 0x71, 0x9b, 0x9b, 0x62, 0xb6, 0xce, 0xd5, 0xb5, 0x2f, 0xa5, 0x18, - 0xfe, 0x47, 0x27, 0x4d, 0xc6, 0xe2, 0x2e, 0x5f, 0x83, 0x98, 0xd4, 0x2a, 0xb2, 0x35, 0x72, 0xcd, - 0x66, 0x23, 0xec, 0x30, 0x7f, 0xb7, 0xef, 0x61, 0x89, 0x2a, 0xe3, 0x19, 0x6c, 0x48, 0x12, 0x56, - 0x05, 0xf4, 0xf0, 0x9b, 0x9c, 0x0f, 0x0d, 0xbb, 0xfb, 0x9d, 0x90, 0x15, 0xd3, 0x25, 0xc5, 0x46, - 0xcf, 0x3b, 0x1d, 0xc8, 0x86, 0xab, 0x0d, 0xef, 0xe2, 0xc3, 0x5c, 0xd3, 0x9e, 0x2a, 0xfd, 0x13, - 0xae, 0xa7, 0x4f, 0x78, 0x7b, 0x41, 0x08, 0x83, 0xeb, 0x72, 0xdb, 0x8c, 0xa7, 0xd2, 0x25, 0xe6, - 0xfe, 0x81, 0x43, 0x69, 0x5b, 0xc0, 0xfe, 0x55, 0x6c, 0x6c, 0xdd, 0xa4, 0x93, 0xf9, 0x2e, 0xd6, - 0x2a, 0x38, 0xe8, 0xf9, 0x0c, 0xc4, 0xc7, 0x13, 0xa4, 0x79, 0x89, 0x84, 0x00, 0x7a, 0x5c, 0xbf, - 0x68, 0xf0, 0xcd, 0xcc, 0x01, 0x27, 0x4c, 0x96, 0xa1, 0x5c, 0x3c, 0x43, 0x55, 0xad, 0xfc, 0x75, - 0xf7, 0xdb, 0x06, 0x03, 0x91, 0x8e, 0xd6, 0xcb, 0x4e, 0x44, 0x91, 0xa8, 0x56, 0x9f, 0xb1, 0xa9, - 0xaf, 0x32, 0x52, 0x8f, 0x6e, 0x26, 0xb2, 0x30, 0x3c, 0x79, 0x87, 0xe3, 0x90, 0x15, 0x57, 0x35, - 0x21, 0xe0, 0x24, 0xb2, 0x23, 0x0b, 0xf1, 0xbb, 0x80, 0x3b, 0xf2, 0xb3, 0x58, 0x88, 0x0f, 0x28, - 0x40, 0xcc, 0x58, 0xac, 0xe3, 0x1e, 0x55, 0x1c, 0x8e, 0x2a, 0x0f, 0x83, 0x49, 0x57, 0x07, 0x41, - 0xf8, 0xe9, 0x30, 0x66, 0xc8, 0xf1, 0x76, 0x08, 0xde, 0x35, 0xae, 0xc3, 0x42, 0x41, 0x5a, 0x4b, - 0xfd, 0xd1, 0x3a, 0x97, 0x57, 0x50, 0x1f, 0x52, 0x46, 0xec, 0xa4, 0x99, 0x13, 0xd0, 0xf0, 0x2e, - 0xff, 0x35, 0xf4, 0xb3, 0xd4, 0x0d, 0xf9, 0x7a, 0x64, 0xb0, 0xfd, 0x02, 0x70, 0x9c, 0x08, 0x32, - 0xc4, 0x23, 0x54, 0x3a, 0x85, 0x07, 0x76, 0xed, 0x9c, 0x05, 0x74, 0xc2, 0x6c, 0xda, 0xb8, 0xdb, - 0x37, 0x1f, 0x5f, 0x4b, 0x73, 0x1e, 0x6f, 0x51, 0x0b, 0xc0, 0x9f, 0xce, 0x3d, 0xbb, 0x58, 0xbb, - 0xae, 0x10, 0x74, 0xcc, 0xac, 0x28, 0x4d, 0x5f, 0x80, 0x68, 0xd2, 0x43, 0x5e, 0x05, 0x3a, 0x03, - 0x7e, 0x3a, 0xbd, 0xc9, 0x57, 0xb7, 0x43, 0x39, 0xef, 0x00, 0x63, 0xd3, 0x67, 0xf7, 0x55, 0x6e, - 0x49, 0xd2, 0xc7, 0xa7, 0xde, 0x36, 0x16, 0xf5, 0xe8, 0xb7, 0x03, 0xb7, 0x58, 0x5f, 0x52, 0x6e, - 0x93, 0x06, 0xc5, 0x93, 0x29, 0xa1, 0x8e, 0x76, 0xb8, 0x53, 0x03, 0xc3, 0x1e, 0x36, 0x16, 0x83, - 0x1f, 0x2a, 0xa2, 0xb1, 0x73, 0xcd, 0x0a, 0x43, 0xcd, 0xe7, 0x6a, 0xe5, 0x2e, 0xfb, 0x8c, 0xf3, - 0xc3, 0x81, 0x09, 0x7d, 0x7a, 0x75, 0x98, 0xec, 0x83, 0x3a, 0x75, 0xe9, 0x7a, 0xe4, 0xdd, 0x53, - 0x9c, 0x31, 0xb3, 0xe4, 0x52, 0x16, 0xef, 0x9c, 0x84, 0x40, 0x08, 0xc2, 0x8d, 0x12, 0xb1, 0x2d, - 0x3c, 0x7e, 0xa7, 0xfb, 0x89, 0xb3, 0xb5, 0xd2, 0x0e, 0xf5, 0xc7, 0x63, 0x6a, 0x8e, 0x83, 0xd6, - 0xa9, 0xf8, 0xf7, 0xdf, 0x29, 0xc9, 0x71, 0x6b, 0x7d, 0x92, 0x31, 0x30, 0x48, 0xa7, 0xe1, 0x4b, - 0x9e, 0xdf, 0x77, 0xfe, 0xba, 0x87, 0x4b, 0x94, 0x10, 0xdc, 0xae, 0xc4, 0x81, 0x6d, 0x2f, 0xb5, - 0x70, 0x50, 0x69, 0x6f, 0x5d, 0x4f, 0x0d, 0xb4, 0x7c, 0x61, 0x83, 0xd4, 0x47, 0xc5, 0xaf, 0xbc, - 0xb9, 0x48, 0xd0, 0x99, 0x44, 0xf4, 0x27, 0x13, 0x68, 0x54, 0x2e, 0x37, 0x7d, 0xee, 0xcd, 0x00, - 0x67, 0x79, 0xf6, 0xf7, 0x64, 0x9b, 0x94, 0x6e, 0x79, 0xbb, 0xf2, 0xe1, 0x9a, 0xb4, 0xe9, 0x3f, - 0x14, 0x2b, 0xb2, 0x4e, 0x6d, 0x96, 0x2f, 0x66, 0x48, 0xcd, 0x0b, 0xf1, 0xbe, 0x87, 0x6f, 0x6f, - 0x43, 0x1e, 0x88, 0x31, 0x05, 0xc3, 0xe8, 0x6b, 0x97, 0x3e, 0x30, 0x73, 0x5a, 0x6f, 0xe1, 0x73, - 0x46, 0xec, 0xbe, 0xed, 0x5d, 0x10, 0x1e, 0xf0, 0xaa, 0xfb, 0xbb, 0x4c, 0x07, 0x2b, 0xad, 0xaa, - 0x2f, 0x4d, 0x51, 0x7c, 0x27, 0x2f, 0x2b, 0xd2, 0x19, 0x0f, 0x5f, 0x8c, 0x87, 0xd9, 0x61, 0x04, - 0x1a, 0x12, 0x1d, 0x6b, 0x31, 0x9e, 0xbc, 0x95, 0x9a, 0xb0, 0x60, 0x30, 0x96, 0x01, 0x7b, 0xf7, - 0xcb, 0xb7, 0x4d, 0xbc, 0x74, 0xfb, 0x86, 0x63, 0x58, 0xef, 0x8c, 0x1a, 0xa5, 0xb9, 0xa1, 0x0b, - 0x2c, 0x52, 0x4e, 0x82, 0x22, 0x78, 0x62, 0x7c, 0x0a, 0xc5, 0x77, 0xae, 0x93, 0x83, 0xc2, 0xa5, - 0x29, 0x05, 0x45, 0x28, 0xe5, 0x8d, 0x56, 0x69, 0x95, 0x40, 0xe4, 0x93, 0x1b, 0xc6, 0x5a, 0x12, - 0x5e, 0xf4, 0xb4, 0x59, 0xa7, 0xa5, 0xfc, 0x10, 0xe0, 0x32, 0x4a, 0xcb, 0x64, 0x1b, 0xa3, 0x3c, - 0x83, 0xa7, 0xe1, 0x00, 0xcb, 0x00, 0xff, 0xe0, 0xab, 0xf6, 0x28, 0x1f, 0x89, 0x42, 0xae, 0x54, - 0x90, 0xdc, 0xca, 0x37, 0xbf, 0x46, 0x4d, 0x0b, 0x06, 0x10, 0x28, 0xe4, 0x6e, 0x02, 0x5f, 0xbd, - 0x5f, 0x6d, 0xd6, 0xca, 0x1b, 0xeb, 0xee, 0x28, 0x2b, 0x74, 0x9f, 0xf5, 0xb0, 0x02, 0x21, 0x09, - 0x16, 0xb9, 0xcd, 0xc4, 0xd9, 0x03, 0xa9, 0x59, 0x72, 0xfc, 0xec, 0xb9, 0x29, 0x2a, 0x55, 0x4a, - 0xeb, 0xd8, 0x21, 0xab, 0x4c, 0xc5, 0x0d, 0x5e, 0xa0, 0xa3, 0x3a, 0x69, 0x9f, 0xbf, 0x07, 0xf2, - 0xca, 0x52, 0x0f, 0xc4, 0x4d, 0x4a, 0x95, 0x81, 0x18, 0x22, 0x90, 0x29, 0x4a, 0x8f, 0xcc, 0x66, - 0xde, 0x3f, 0xb5, 0xb7, 0xf5, 0xec, 0x5b, 0x26, 0x7d, 0x1a, 0x0e, 0x6f, 0x84, 0xc2, 0x7c, 0x02, - 0x25, 0x36, 0x49, 0xf5, 0x90, 0x33, 0x89, 0xa0, 0x90, 0x17, 0x97, 0xa7, 0xec, 0x8b, 0x78, 0xaf, - 0x12, 0xbf, 0x2b, 0x1c, 0xc2, 0x2d, 0x29, 0x13, 0x47, 0xa0, 0xf8, 0xd6, 0xeb, 0x26, 0x98, 0xaf, - 0xca, 0x7c, 0xb4, 0xc2, 0x3e, 0xdf, 0x25, 0x0a, 0x5b, 0x77, 0xe5, 0x1e, 0xff, 0xa3, 0xc6, 0x75, - 0xf1, 0xfa, 0x95, 0x1d, 0x72, 0x46, 0x7e, 0xda, 0xf9, 0x87, 0xd5, 0xb6, 0x3d, 0xe4, 0x17, 0x75, - 0x35, 0x97, 0x4b, 0xb2, 0x00, 0x6a, 0xa9, 0xa8, 0xdd, 0x9e, 0x62, 0x41, 0x08, 0x22, 0x9b, 0x1c, - 0x57, 0x35, 0xbb, 0x03, 0x9c, 0xce, 0x47, 0x16, 0x2e, 0xb5, 0x2d, 0xe8, 0x87, 0x03, 0xa8, 0xbd, - 0xb8, 0xed, 0x40, 0x9b, 0xc0, 0x1d, 0xab, 0xad, 0xfd, 0x31, 0xeb, 0xb9, 0x5c, 0xad, 0x90, 0x26, - 0x70, 0x5d, 0xba, 0xe5, 0xa2, 0xd0, 0x63, 0x76, 0x32, 0x0b, 0xcf, 0xac, 0x17, 0xa7, 0xda, 0xc9, - 0x57, 0xbf, 0xee, 0x98, 0x48, 0x0c, 0x7a, 0x11, 0x1b, 0x5e, 0xd3, 0x00, 0x44, 0x7a, 0x21, 0xfe, - 0x62, 0x44, 0x8f, 0x62, 0xcc, 0xff, 0x7d, 0xa6, 0x86, 0x89, 0xa7, 0x3a, 0x2e, 0xc7, 0x0b, 0xd1, - 0x31, 0x30, 0x33, 0x7e, 0xab, 0xf2, 0x6a, 0x00, 0xbf, 0x86, 0xe5, 0x20, 0x77, 0xea, 0x00, 0xda, - 0x46, 0xfe, 0x73, 0x23, 0x78, 0x0c, 0xf9, 0x8b, 0x3e, 0x99, 0x8b, 0xb3, 0x0c, 0x39, 0x98, 0x01, - 0x7f, 0x4b, 0xea, 0x45, 0x55, 0x68, 0xfc, 0xd5, 0x67, 0x08, 0x6e, 0xc6, 0x8a, 0xbc, 0x40, 0x11, - 0x3d, 0xb4, 0x74, 0xc6, 0xc8, 0x8b, 0x1c, 0x1c, 0x75, 0x88, 0xe7, 0xa8, 0xc8, 0xd2, 0x08, 0x2d, - 0x4c, 0x70, 0xdc, 0xb8, 0xfc, 0x3d, 0x8f, 0xba, 0xc5, 0x3c, 0xae, 0x95, 0x28, 0x6f, 0x13, 0x25, - 0x42, 0x53, 0x86, 0x15, 0x67, 0xe4, 0x71, 0x5b, 0x03, 0xe6, 0x82, 0xe4, 0x22, 0xc1, 0x11, 0x16, - 0x9d, 0x96, 0x35, 0x9e, 0xe1, 0x82, 0x65, 0xc3, 0xca, 0xdb, 0xab, 0x3a, 0xe6, 0x1f, 0x03, 0xf8, - 0x6d, 0x09, 0x6a, 0xda, 0xe9, 0xa6, 0xc4, 0x82, 0x0e, 0xbe, 0x3b, 0x61, 0x01, 0x59, 0x95, 0x70, - 0x76, 0x95, 0xb4, 0x2b, 0x67, 0x80, 0x2f, 0xac, 0x2d, 0x6f, 0xbb, 0x2b, 0x67, 0x80, 0x1a, 0x3e, - 0x48, 0x84, 0x3e, 0xb8, 0x32, 0x88, 0xab, 0x52, 0xc2, 0x9c, 0x1f, 0x11, 0x2f, 0x92, 0x33, 0x86, - 0x06, 0x12, 0xba, 0x01, 0x5d, 0x4b, 0x7c, 0xab, 0xc1, 0x9c, 0x62, 0x94, 0xf8, 0x87, 0x1e, 0x2e, - 0xcf, 0x6a, 0xf3, 0x34, 0xdf, 0x9c, 0x83, 0x30, 0x97, 0x12, 0x77, 0xe3, 0x2c, 0xf1, 0x48, 0x8f, - 0x04, 0xb4, 0xb2, 0x85, 0xae, 0xea, 0xa8, 0x3b, 0xb8, 0x1d, 0x54, 0xb6, 0xb8, 0x42, 0x64, 0x39, - 0x80, 0x4b, 0x02, 0x78, 0x22, 0x37, 0x3a, 0xb9, 0x98, 0x68, 0xec, 0x06, 0x87, 0x35, 0xc8, 0x16, - 0x52, 0x1b, 0xd7, 0x5d, 0xc1, 0x9f, 0x79, 0xd2, 0xe2, 0x16, 0x6b, 0x1a, 0x55, 0x1c, 0x99, 0x83, - 0x18, 0xfe, 0x19, 0xf5, 0x07, 0x9b, 0xaa, 0xdb, 0x12, 0x73, 0x25, 0x28, 0xbc, 0x8b, 0x52, 0x24, - 0x38, 0xaa, 0xd2, 0x52, 0xc5, 0x7d, 0x38, 0x99, 0x40, 0x09, 0x13, 0xcd, 0x12, 0x4d, 0x2d, 0xbe, - 0x90, 0xa8, 0x91, 0xd9, 0x59, 0x00, 0xc0, 0x25, 0x6d, 0x9d, 0xdb, 0xf9, 0x39, 0xba, 0xdc, 0xf6, - 0xe3, 0x7e, 0x67, 0x83, 0xda, 0x2a, 0x2b, 0xaa, 0xd8, 0x72, 0xf6, 0xc8, 0xc1, 0x18, 0x78, 0xed, - 0x6d, 0x5e, 0xe2, 0x98, 0x81, 0x35, 0xa3, 0x9f, 0x2d, 0xac, 0x7c, 0xf1, 0x27, 0x10, 0x0c, 0x36, - 0xd2, 0xd4, 0xe5, 0x8b, 0x3a, 0x9b, 0x1d, 0x73, 0x41, 0x12, 0x0f, 0xfc, 0xa0, 0xee, 0xd2, 0xe9, - 0x8e, 0x02, 0x0a, 0x46, 0xf1, 0xae, 0xcb, 0xc8, 0x09, 0xa2, 0x1e, 0xf7, 0x9e, 0x6d, 0xbc, 0xe7, - 0x6f, 0x96, 0xb6, 0x93, 0x01, 0x07, 0xf0, 0x78, 0xa5, 0xd4, 0x46, 0x4c, 0xfe, 0xac, 0x41, 0x4c, - 0xa4, 0x1a, 0x5e, 0xe4, 0xf8, 0x7c, 0xcd, 0x88, 0x68, 0xe7, 0x92, 0x2a, 0x14, 0xc2, 0x47, 0x88, - 0x5f, 0x0d, 0xa9, 0xe2, 0x3a, 0x76, 0x04, 0xed, 0xe7, 0x03, 0xc2, 0xbe, 0x36, 0x33, 0xaf, 0x9c, - 0xcc, 0x32, 0xb9, 0xe5, 0xf5, 0x1c, 0xf5, 0x19, 0x78, 0xf1, 0x0a, 0x06, 0xf5, 0xd7, 0x7c, 0x67, - 0x92, 0x7d, 0x80, 0xae, 0x48, 0x0e, 0x7e, 0x5e, 0x00, 0x54, 0x01, 0xed, 0x95, 0x46, 0x79, 0x72, - 0xed, 0x87, 0x02, 0x90, 0x29, 0xda, 0x05, 0xdf, 0xbc, 0xf2, 0xf0, 0xb9, 0xd5, 0x04, 0x41, 0xe8, - 0xf1, 0x58, 0x04, 0xb0, 0xa5, 0x9d, 0x23, 0xcf, 0xc2, 0xe3, 0xc0, 0xfb, 0xfc, 0xb1, 0x0b, 0x69, - 0xaa, 0xdf, 0x0b, 0x37, 0xc0, 0x6d, 0xbf, 0x97, 0x2e, 0x26, 0x4c, 0x6c, 0xde, 0x41, 0x42, 0xc0, - 0x4f, 0xfd, 0x9f, 0xa0, 0xaf, 0x24, 0x51, 0xf8, 0xbe, 0xc3, 0xfd, 0xe9, 0x72, 0x33, 0x06, 0x08, - 0x5e, 0xce, 0xfb, 0x17, 0xc5, 0x91, 0xe6, 0x91, 0xf9, 0xb4, 0xd4, 0xf7, 0x96, 0xe2, 0x93, 0xc0, - 0xcd, 0x32, 0x2f, 0x02, 0xb9, 0x69, 0x38, 0xa5, 0xf7, 0xd1, 0xc8, 0xa7, 0xfe, 0xb9, 0x4c, 0x03, - 0x1d, 0xe2, 0x4e, 0x71, 0x02, 0x0f, 0xf3, 0x7e, 0x00, 0x59, 0x0a, 0xe7, 0xca, 0x48, 0xca, 0x87, - 0x6a, 0xfc, 0xa2, 0xbe, 0x37, 0x23, 0xea, 0xad, 0x40, 0xfd, 0x27, 0xb9, 0x0f, 0xe6, 0xea, 0x03, - 0x9c, 0x9f, 0x3f, 0x60, 0x69, 0xfd, 0xbc, 0x70, 0xf1, 0x66, 0x26, 0xa4, 0x16, 0x78, 0xa3, 0x1e, - 0x8a, 0xd8, 0x8d, 0xe9, 0xbc, 0xe8, 0xa2, 0x5d, 0x43, 0xb8, 0x6e, 0xbf, 0x80, 0xe5, 0xe4, 0x26, - 0xfe, 0x21, 0x60, 0x6e, 0x67, 0x54, 0x5f, 0xf8, 0xe5, 0x5f, 0xa7, 0xd9, 0x80, 0x2d, 0x65, 0xaf, - 0xbd, 0xa7, 0x9e, 0xf4, 0x0d, 0xe9, 0x24, 0xfd, 0x0d, 0xa7, 0xb5, 0xc8, 0xf6, 0xa2, 0xcf, 0x99, - 0x3f, 0xa6, 0xd2, 0x7b, 0x28, 0x61, 0xc6, 0x29, 0xcc, 0x9b, 0xd2, 0xe1, 0xe6, 0xaf, 0x04, 0xf8, - 0x80, 0x65, 0x61, 0x4f, 0x7e, 0x24, 0xcd, 0xd1, 0x9c, 0x88, 0xcf, 0x2d, 0x7d, 0x2b, 0x93, 0xb7, - 0x40, 0x8d, 0x89, 0x85, 0x25, 0xe5, 0x14, 0x1f, 0xf7, 0x1d, 0x18, 0xda, 0xba, 0xc1, 0x86, 0xdc, - 0xd4, 0x32, 0xf3, 0x89, 0x5a, 0x10, 0x27, 0xc9, 0x2c, 0xf8, 0x61, 0x3c, 0x80, 0xbd, 0x46, 0xe4, - 0x75, 0x8c, 0xa2, 0x6c, 0x8c, 0xd9, 0x2b, 0x74, 0xee, 0xbc, 0x5f, 0xa5, 0xed, 0xf9, 0xc9, 0xfe, - 0xc5, 0x4f, 0xea, 0xc4, 0x0f, 0x09, 0x44, 0xe3, 0xfa, 0x16, 0x5e, 0x51, 0x5a, 0x03, 0xdb, 0x57, - 0xc2, 0x05, 0xd3, 0x83, 0xc0, 0x4b, 0x2c, 0xbf, 0xf7, 0x03, 0x93, 0x9e, 0xf3, 0x21, 0xdf, 0x8a, - 0x49, 0xbc, 0x3f, 0x5b, 0x14, 0x09, 0xea, 0x49, 0x10, 0x62, 0xb7, 0xb9, 0x98, 0x22, 0x77, 0x82, - 0x5b, 0x52, 0xc6, 0xcc, 0x83, 0x13, 0xba, 0x05, 0xab, 0xff, 0xcd, 0xfa, 0x52, 0x54, 0x45, 0xb1, - 0xcf, 0xf7, 0x73, 0xd2, 0x85, 0x36, 0xa9, 0x89, 0xd2, 0x57, 0x96, 0x7b, 0x50, 0xd7, 0xbe, 0xc4, - 0xfd, 0xe5, 0xf3, 0x38, 0xd2, 0xae, 0x81, 0xbc, 0x8e, 0xc5, 0x92, 0xbe, 0x93, 0xbd, 0xa0, 0xa7, - 0xdd, 0x08, 0x90, 0x57, 0xe6, 0x75, 0x67, 0x4e, 0x8b, 0x99, 0x7b, 0x05, 0xd0, 0x49, 0xca, 0x3d, - 0xbd, 0x60, 0xb7, 0xf3, 0xfb, 0x23, 0xba, 0x5f, 0x4e, 0x5a, 0x48, 0xed, 0x85, 0x35, 0x23, 0x4e, - 0x5c, 0x66, 0xc4, 0x1b, 0x15, 0x7c, 0x25, 0xb5, 0xd9, 0xef, 0x5c, 0x8c, 0x0e, 0xd0, 0x16, 0xd6, - 0xb2, 0x17, 0xf4, 0xb6, 0x0b, 0xfd, 0x0d, 0xa2, 0x23, 0xa5, 0xd4, 0x0c, 0x2a, 0x40, 0x3d, 0xe8, - 0x26, 0x8a, 0xd5, 0xe6, 0x20, 0x7c, 0x54, 0xb2, 0x17, 0xc2, 0xf0, 0x09, 0xc5, 0xdd, 0x05, 0x22, - 0x66, 0x72, 0x2d, 0xc2, 0x1d, 0x4a, 0xd8, 0x31, 0x4d, 0xe4, 0x98, 0x69, 0x2e, 0x3c, 0x77, 0xc9, - 0xf8, 0x22, 0x6a, 0x14, 0xf2, 0x7d, 0x49, 0xe0, 0xda, 0x11, 0x01, 0xe3, 0x2e, 0x5b, 0x86, 0xbc, - 0x0f, 0x03, 0x48, 0xa2, 0x0a, 0x36, 0x0a, 0x91, 0x9e, 0xc8, 0x63, 0x69, 0x5d, 0x2e, 0xc3, 0x18, - 0xa6, 0x1f, 0x65, 0x4c, 0x9a, 0x15, 0x26, 0xaf, 0x15, 0x38, 0xf9, 0x28, 0xdb, 0x76, 0xf9, 0xb1, - 0x4e, 0xf5, 0x98, 0xf2, 0x55, 0x1e, 0xe8, 0x5d, 0x6e, 0x04, 0x29, 0x19, 0x2c, 0x5f, 0x18, 0xd3, - 0x39, 0xdd, 0x5a, 0x28, 0x0d, 0xd5, 0xb1, 0xa8, 0xf5, 0x9e, 0xc1, 0xf2, 0x94, 0x79, 0x96, 0xc7, - 0xe1, 0x90, 0xc8, 0x75, 0x02, 0xfd, 0xf5, 0x00, 0x47, 0x72, 0x22, 0x4b, 0xbd, 0x1c, 0x4d, 0xd6, - 0x2a, 0x8d, 0xa1, 0x72, 0x3e, 0xdb, 0x41, 0x17, 0x6a, 0x9a, 0xf0, 0x4b, 0x1f, 0x30, 0xd0, 0x90, - 0x50, 0xe7, 0x35, 0x79, 0x36, 0x43, 0x60, 0xdb, 0x4f, 0x98, 0x03, 0x4f, 0xec, 0x1b, 0x43, 0xe6, - 0xe9, 0xe0, 0x22, 0x64, 0x09, 0xe6, 0xaf, 0x16, 0x09, 0xfe, 0xb8, 0x62, 0xd8, 0x45, 0x99, 0x4b, - 0xc2, 0x0e, 0x67, 0x01, 0x02, 0x37, 0xb7, 0x4d, 0xf1, 0xab, 0x59, 0xed, 0xf1, 0x4b, 0x16, 0x23, - 0xfd, 0xc9, 0x90, 0xb8, 0x7b, 0x92, 0x9f, 0xb7, 0xe8, 0x37, 0xb1, 0xed, 0x90, 0x52, 0x86, 0xa8, - 0x86, 0xf5, 0x9b, 0x02, 0x3f, 0x40, 0xb0, 0x7a, 0x98, 0x9e, 0xf7, 0xa9, 0x9e, 0x97, 0x4e, 0xba, - 0xec, 0x49, 0xb4, 0x5f, 0xee, 0x1f, 0x40, 0x5d, 0x11, 0xa6, 0x41, 0xb2, 0xb1, 0x50, 0xeb, 0xe8, - 0x54, 0xf7, 0x85, 0x3f, 0x06, 0x02, 0x6c, 0xb1, 0x28, 0x7f, 0xd8, 0xbc, 0xef, 0xb1, 0x5d, 0x2a, - 0xea, 0x62, 0x3d, 0x7c, 0x3a, 0xda, 0xa0, 0x0b, 0x35, 0xa1, 0x05, 0xab, 0xf2, 0xd8, 0xf9, 0x71, - 0x5b, 0xe6, 0x5a, 0xfc, 0xc1, 0x38, 0x1d, 0x29, 0xc5, 0xef, 0x39, 0x8f, 0x2b, 0x76, 0xfe, 0x16, - 0x51, 0x34, 0x8b, 0xaa, 0xc1, 0x47, 0xcf, 0xaf, 0xf3, 0x47, 0x78, 0x24, 0x2b, 0x32, 0xa1, 0x62, - 0x0e, 0x03, 0x32, 0xee, 0xc7, 0xaf, 0x5e, 0x75, 0x0d, 0xfe, 0xc3, 0x0a, 0x89, 0x94, 0x52, 0xad, - 0xc5, 0x30, 0xdd, 0x93, 0x3a, 0x07, 0x74, 0x8d, 0xd5, 0x22, 0xac, 0x20, 0xae, 0x2b, 0x28, 0x89, - 0xfa, 0xc3, 0x23, 0x7d, 0x23, 0xc4, 0x47, 0x2f, 0xc8, 0xc6, 0xb2, 0x36, 0xd4, 0xa4, 0xd1, 0x9f, - 0x67, 0x26, 0x4f, 0x54, 0xbb, 0x9b, 0x25, 0x31, 0x81, 0x56, 0xb3, 0x0e, 0xdf, 0x27, 0x3b, 0xbf, - 0x35, 0x98, 0xaf, 0x5b, 0x34, 0xba, 0xd4, 0x15, 0x85, 0x98, 0x2f, 0x60, 0x39, 0xf6, 0xe4, 0x88, - 0x30, 0x8b, 0x47, 0xea, 0xea, 0x5d, 0x21, 0xa9, 0x1e, 0x58, 0x38, 0xf7, 0xd9, 0xcc, 0xc8, 0xf7, - 0x26, 0xeb, 0x2d, 0xb8, 0x2d, 0x02, 0x2b, 0xaf, 0x10, 0x3a, 0xb2, 0xdd, 0x44, 0x44, 0x89, 0xb9, - 0x4e, 0x8d, 0xdb, 0xa7, 0x1e, 0x62, 0xab, 0x62, 0x51, 0xdd, 0x5d, 0x5f, 0x69, 0xe4, 0x4f, 0x37, - 0x76, 0x74, 0xa5, 0xc2, 0xb0, 0x32, 0x15, 0x0c, 0x32, 0x66, 0x4c, 0x96, 0xc0, 0x72, 0x86, 0x7b, - 0xb7, 0xa2, 0xdc, 0x29, 0x03, 0x3e, 0x6b, 0xcc, 0xae, 0xdd, 0xea, 0x43, 0x0e, 0x5e, 0x15, 0x5d, - 0xd0, 0x72, 0x4f, 0xcb, 0xc0, 0x41, 0x5b, 0x2a, 0xb6, 0xdf, 0xe6, 0x8a, 0x2e, 0x59, 0x40, 0x3b, - 0x20, 0x6b, 0xbe, 0x7f, 0x42, 0x1f, 0xaa, 0xc5, 0x3f, 0x66, 0x04, 0x0c, 0x5c, 0xcc, 0xeb, 0x19, - 0xf1, 0x7b, 0xf0, 0x00, 0xae, 0xa7, 0xe7, 0x75, 0x21, 0x1e, 0x48, 0xae, 0x04, 0xab, 0x32, 0xb2, - 0xea, 0x47, 0x0f, 0xf9, 0x77, 0x07, 0xd9, 0xc0, 0x73, 0xfe, 0x72, 0xa7, 0xb7, 0x7d, 0x49, 0xff, - 0x8d, 0xea, 0x36, 0x61, 0x0c, 0x2a, 0x83, 0x30, 0x64, 0x35, 0x86, 0x1b, 0xe0, 0x1d, 0xc4, 0xb2, - 0x0d, 0xbb, 0x8a, 0xa6, 0xcb, 0x78, 0x03, 0x28, 0xdc, 0xfc, 0x2a, 0x32, 0xd7, 0x4c, 0xab, 0xa8, - 0x39, 0x29, 0xcb, 0xc8, 0x5e, 0x40, 0x6e, 0x74, 0xee, 0xdb, 0x72, 0x7a, 0x2f, 0xb5, 0xbd, 0x9d, - 0x30, 0x4a, 0xf4, 0x62, 0x25, 0x83, 0x28, 0x58, 0xb2, 0x95, 0xa1, 0x7b, 0x9e, 0x71, 0x0b, 0xd7, - 0xb0, 0xb5, 0xd1, 0x9d, 0xbb, 0xf4, 0xfd, 0x02, 0x1e, 0xaf, 0x29, 0xf4, 0xd1, 0xb6, 0xbd, 0x7e, - 0x78, 0xc8, 0xa3, 0xe3, 0x23, 0x5b, 0x5d, 0x0b, 0xc6, 0x87, 0xbd, 0x5b, 0x42, 0x8d, 0x23, 0x18, - 0xcf, 0x43, 0x30, 0xec, 0x34, 0x35, 0x66, 0xd4, 0x2d, 0x17, 0xbe, 0x83, 0x8e, 0xee, 0xdf, 0x70, - 0xf1, 0xe4, 0x09, 0xc1, 0x89, 0xdb, 0x1a, 0xe0, 0xa2, 0x05, 0xd2, 0x22, 0xc9, 0x2a, 0x1a, 0xdd, - 0x33, 0xb8, 0xe3, 0xb1, 0x26, 0xaf, 0x62, 0x81, 0x8a, 0x2d, 0xbe, 0x3e, 0x3c, 0x00, 0x10, 0x98, - 0xa9, 0x86, 0x86, 0x5c, 0x6b, 0x57, 0xd0, 0xb2, 0xf5, 0x57, 0x9b, 0x97, 0xb6, 0xf1, 0x01, 0xeb, - 0x56, 0xfa, 0x83, 0x9f, 0x87, 0x89, 0xf6, 0x77, 0x40, 0x74, 0x18, 0x5a, 0x8e, 0x0f, 0x38, 0x3e, - 0x9d, 0x88, 0x13, 0xca, 0x16, 0x70, 0xd8, 0xb7, 0x58, 0x6a, 0x3a, 0x1c, 0x27, 0xa3, 0xf5, 0xa3, - 0x87, 0x4c, 0x4e, 0x74, 0x3e, 0xae, 0x21, 0xbc, 0x51, 0x9d, 0xcc, 0x43, 0x0e, 0x28, 0x1d, 0x2f, - 0x73, 0xc2, 0x3b, 0xab, 0x94, 0x89, 0x38, 0xa4, 0x6f, 0xc2, 0x4d, 0xa9, 0x46, 0x47, 0x15, 0x0d, - 0xb7, 0x27, 0x83, 0xab, 0x3f, 0xac, 0x01, 0xd8, 0x34, 0xa8, 0x54, 0x4f, 0x14, 0x47, 0x79, 0xa5, - 0x14, 0x80, 0x64, 0x3c, 0xb1, 0xac, 0xca, 0x0b, 0xf8, 0x8d, 0x20, 0x3f, 0x86, 0x56, 0x64, 0xa5, - 0xec, 0xe1, 0x8a, 0x88, 0xce, 0x31, 0xb0, 0xd8, 0xfe, 0xec, 0x58, 0x5f, 0x95, 0x31, 0x32, 0x90, - 0x02, 0xd4, 0xff, 0xab, 0xed, 0x01, 0xef, 0x42, 0xaf, 0x52, 0xdd, 0xf0, 0x84, 0x00, 0xc5, 0x8d, - 0x07, 0xe9, 0x80, 0x46, 0xd7, 0x7c, 0x7a, 0x14, 0x2b, 0x51, 0x3d, 0xa0, 0xee, 0x79, 0x5d, 0xd5, - 0x7e, 0xa4, 0x3f, 0x4c, 0xe3, 0x6c, 0x8f, 0x6d, 0x28, 0x62, 0x04, 0x49, 0x45, 0xdc, 0x29, 0x3c, - 0x8d, 0x47, 0xd9, 0xf2, 0x07, 0xb0, 0x34, 0x42, 0x20, 0xe3, 0x17, 0x7d, 0x6e, 0x17, 0xa4, 0xb3, - 0x9b, 0x22, 0xcc, 0xae, 0x3c, 0x53, 0xd4, 0xe0, 0xdc, 0x87, 0xf7, 0x0c, 0xbe, 0x9b, 0xf7, 0x43, - 0xce, 0xac, 0xb8, 0x93, 0x2d, 0xf8, 0xf8, 0x4a, 0x52, 0x7c, 0x9f, 0xb8, 0x4c, 0x6c, 0xf5, 0x20, - 0x6e, 0xc2, 0xe4, 0x3d, 0x1f, 0xf8, 0xe8, 0x46, 0xda, 0x06, 0xeb, 0xff, 0xfb, 0x87, 0xbe, 0xc0, - 0x9c, 0x63, 0xe9, 0x63, 0x37, 0x13, 0x76, 0x80, 0x60, 0xec, 0xa6, 0x25, 0x9a, 0xaf, 0xe7, 0xc7, - 0x87, 0xf3, 0x94, 0x7d, 0x5b, 0x94, 0xa6, 0xb8, 0x0f, 0xf0, 0xa7, 0xa4, 0x78, 0x58, 0x36, 0xf9, - 0xa1, 0x39, 0x68, 0x33, 0x34, 0x7c, 0x13, 0x03, 0x40, 0x40, 0xd1, 0xc6, 0xb1, 0xf8, 0x37, 0x82, - 0xaa, 0x64, 0x58, 0x32, 0x81, 0x60, 0xfa, 0x04, 0x97, 0x1d, 0xe0, 0x4a, 0x16, 0xb6, 0xc0, 0x31, - 0xb2, 0x57, 0x1d, 0xd1, 0x31, 0x91, 0x2c, 0xdf, 0x25, 0x15, 0xdd, 0x56, 0x98, 0x3e, 0xd6, 0xef, - 0x3b, 0xbe, 0x5b, 0x39, 0x89, 0xe9, 0x51, 0xbf, 0x0b, 0x99, 0xc5, 0x60, 0x5c, 0x38, 0x18, 0x1e, - 0x96, 0x69, 0x04, 0x87, 0xe8, 0x72, 0x40, 0x05, 0x9d, 0xe2, 0x1b, 0xd2, 0x20, 0x05, 0x9e, 0x5d, - 0x7e, 0x3c, 0x83, 0x2f, 0xd4, 0x56, 0x9b, 0xb7, 0xdf, 0x06, 0x43, 0x71, 0x97, 0xca, 0xc1, 0x41, - 0xe8, 0xfe, 0x5b, 0x36, 0x9b, 0x54, 0x2c, 0xbe, 0xed, 0x2f, 0x90, 0x25, 0xa0, 0x30, 0xc5, 0x4f, - 0x0b, 0x5d, 0x2b, 0x1e, 0x79, 0x01, 0x1c, 0xf2, 0xa0, 0xa0, 0xdb, 0x14, 0x31, 0x2e, 0x35, 0x2d, - 0x07, 0xbc, 0x03, 0xe6, 0x54, 0xaa, 0xba, 0xa9, 0x5a, 0xfd, 0x30, 0xf1, 0x48, 0x3b, 0x57, 0xbb, - 0x1a, 0x74, 0xa0, 0xcd, 0xf8, 0xc9, 0xfd, 0xeb, 0xe4, 0xb0, 0xe1, 0x56, 0xfe, 0x86, 0xbb, 0x1e, - 0x21, 0xd8, 0x61, 0xf8, 0xeb, 0xe0, 0xc4, 0xab, 0x2b, 0x23, 0xe7, 0x76, 0xec, 0x01, 0xa2, 0x81, - 0xf6, 0xda, 0x9b, 0xec, 0x4c, 0x56, 0x06, 0x68, 0xb4, 0xee, 0xc7, 0x18, 0x56, 0xe6, 0xcc, 0x41, - 0x74, 0x1d, 0xf2, 0xeb, 0xbd, 0x4e, 0x95, 0xb6, 0x61, 0xec, 0x24, 0xfe, 0x31, 0xb1, 0xda, 0xab, - 0xc9, 0x37, 0x3c, 0xcb, 0xc8, 0x2f, 0x3c, 0x13, 0xc3, 0x1e, 0x47, 0x80, 0x97, 0x25, 0x98, 0x50, - 0x22, 0x19, 0x28, 0xc7, 0xac, 0x6b, 0xb9, 0x33, 0xa5, 0x63, 0x1c, 0x89, 0x6a, 0xca, 0x87, 0xfb, - 0x6f, 0x5e, 0x3e, 0xef, 0x36, 0xef, 0x5b, 0x9c, 0xe5, 0x9f, 0x56, 0x76, 0x81, 0x3a, 0x1a, 0x7e, - 0x76, 0x8f, 0xc9, 0xb8, 0xad, 0x4a, 0x0a, 0x07, 0x26, 0x72, 0xba, 0x23, 0xcc, 0x21, 0xb2, 0x1a, - 0x95, 0xc9, 0xbb, 0xb1, 0xa4, 0x0f, 0xdf, 0xeb, 0xd2, 0xd6, 0x11, 0xb7, 0x73, 0xdf, 0xa5, 0x5f, - 0x87, 0xe9, 0xab, 0x1c, 0xe5, 0x1c, 0xc4, 0x2b, 0x0f, 0xe2, 0xfe, 0x15, 0xda, 0x2d, 0x73, 0xf6, - 0x56, 0xca, 0x56, 0x27, 0x9a, 0xa1, 0x67, 0x22, 0xab, 0xae, 0xd5, 0x72, 0x75, 0x19, 0xea, 0x25, - 0x87, 0x94, 0x50, 0x3f, 0x18, 0x08, 0xe4, 0xeb, 0xf0, 0x56, 0x53, 0x0b, 0x31, 0x8b, 0x99, 0xa7, - 0x48, 0x86, 0xd0, 0x5d, 0x04, 0x2d, 0xf4, 0xd8, 0x29, 0xc4, 0x25, 0xc9, 0x08, 0xb5, 0x81, 0x62, - 0xee, 0x30, 0xa0, 0x73, 0xd4, 0xbb, 0x7d, 0x27, 0x27, 0x5b, 0x54, 0x8e, 0x7a, 0x90, 0xe6, 0xd1, - 0x78, 0xdd, 0x27, 0xf6, 0x7c, 0xd9, 0x55, 0x27, 0x08, 0x25, 0xfb, 0x96, 0x30, 0x0c, 0x46, 0x45, - 0xe4, 0xcd, 0x9c, 0x17, 0x5c, 0x11, 0x85, 0x70, 0x7d, 0x4d, 0x36, 0x59, 0x55, 0xfc, 0xfd, 0x24, - 0x5a, 0x55, 0xd6, 0x4e, 0xfb, 0x66, 0x30, 0x8a, 0x47, 0xfc, 0x85, 0x59, 0x2b, 0xfb, 0x75, 0x5e, - 0xb4, 0xaa, 0x8b, 0x78, 0xb5, 0x41, 0x27, 0x4e, 0x2c, 0x22, 0x5d, 0xad, 0x64, 0x9c, 0x24, 0x8e, - 0x7f, 0x27, 0x92, 0xa5, 0x6b, 0xfb, 0xef, 0x70, 0x45, 0x51, 0x58, 0x84, 0x60, 0xb1, 0x92, 0xda, - 0xfb, 0x9b, 0xf6, 0x79, 0x8b, 0x7d, 0x4c, 0xec, 0xfa, 0x42, 0x4a, 0xea, 0xd6, 0x75, 0xdc, 0xa0, - 0x81, 0xe6, 0x0b, 0xbe, 0xd6, 0x70, 0xf9, 0xab, 0x23, 0x16, 0xf5, 0x01, 0x2e, 0x22, 0x90, 0x77, - 0x0d, 0x33, 0x23, 0x40, 0x3d, 0xa5, 0x74, 0x8b, 0xe6, 0x68, 0x15, 0x65, 0xa7, 0x13, 0x87, 0x3a, - 0x53, 0x86, 0xa6, 0xf1, 0x29, 0x9f, 0x0e, 0x1e, 0xdb, 0x34, 0x1f, 0xff, 0x08, 0x32, 0x55, 0x10, - 0xa7, 0xf2, 0xfe, 0x5e, 0xb0, 0x30, 0x27, 0x27, 0xd7, 0x35, 0x5e, 0xe4, 0x8e, 0xd5, 0xdf, 0x43, - 0x16, 0xfd, 0x34, 0x83, 0x32, 0x38, 0x69, 0x0b, 0x91, 0xd4, 0x71, 0x65, 0xcd, 0x4c, 0xc0, 0x45, - 0x4d, 0xa3, 0x3b, 0x87, 0x56, 0x9d, 0x38, 0x88, 0x0a, 0xd9, 0x2a, 0x92, 0xab, 0xa0, 0xf2, 0x4d, - 0x99, 0xed, 0x4c, 0xc9, 0x77, 0xa2, 0xb4, 0xe3, 0x79, 0x54, 0xfb, 0xe9, 0xb9, 0x32, 0x01, 0x01, - 0xc8, 0x0a, 0x85, 0x81, 0x22, 0x79, 0x30, 0x62, 0xc7, 0xac, 0x26, 0xe7, 0x1b, 0xe2, 0x60, 0x8d, - 0xbd, 0x2a, 0x97, 0xcf, 0x4e, 0x88, 0x4f, 0xd3, 0x25, 0x5d, 0x00, 0x87, 0x00, 0xff, 0x35, 0x31, - 0xc0, 0x45, 0x75, 0x0b, 0x2e, 0xaf, 0x77, 0x30, 0xbc, 0x9a, 0x51, 0xc6, 0x75, 0xd9, 0x41, 0x79, - 0xdc, 0xfc, 0xe9, 0xee, 0x63, 0xc1, 0x47, 0x3d, 0x15, 0xb4, 0x31, 0x9a, 0xa9, 0xed, 0xfd, 0x19, - 0x73, 0x9c, 0xc0, 0x5c, 0x5c, 0x3c, 0x8a, 0xa6, 0x18, 0xfa, 0x90, 0x96, 0xc6, 0x64, 0x87, 0x56, - 0xf8, 0xaa, 0x74, 0xfd, 0x18, 0xa8, 0xcd, 0x81, 0xa2, 0xd2, 0x48, 0xb8, 0x56, 0x90, 0x2b, 0x4d, - 0x30, 0x18, 0x2b, 0xfc, 0x2f, 0x39, 0x24, 0x50, 0xf9, 0x02, 0x97, 0x30, 0x43, 0xea, 0x0f, 0x5e, - 0x6c, 0x87, 0x2a, 0xd4, 0x3a, 0x2d, 0x31, 0x9f, 0x99, 0xf0, 0x95, 0x80, 0x8d, 0x83, 0xa8, 0xb0, - 0x2b, 0xd3, 0x80, 0xd6, 0xc3, 0xf3, 0xdd, 0x22, 0x7e, 0xae, 0x2b, 0x4a, 0x48, 0x67, 0x71, 0x38, - 0x53, 0xd6, 0x3c, 0xff, 0x40, 0x56, 0x21, 0xb1, 0xee, 0xee, 0xf0, 0x0d, 0x5b, 0xa2, 0xff, 0x25, - 0x4c, 0xdf, 0x97, 0xb4, 0x22, 0x63, 0x34, 0xa9, 0x86, 0x6a, 0x2b, 0x5b, 0xdf, 0x1f, 0x8e, 0x8a, - 0xfd, 0xf1, 0x23, 0x8d, 0xa9, 0xf4, 0x8f, 0xc3, 0x88, 0xa3, 0x39, 0xf7, 0x9d, 0x64, 0x8c, 0xaa, - 0x0a, 0x0a, 0x69, 0xd6, 0xf1, 0x6e, 0x29, 0x76, 0x63, 0xe5, 0xf4, 0xcf, 0x1a, 0xa9, 0x54, 0x27, - 0xe6, 0x8d, 0x71, 0xe7, 0x4c, 0x0a, 0x2f, 0xb6, 0xb6, 0x89, 0x22, 0x4b, 0xd9, 0x97, 0x8f, 0x7a, - 0x3a, 0x45, 0x05, 0x33, 0x47, 0x74, 0x17, 0x6f, 0xd8, 0x70, 0x97, 0xd8, 0xab, 0x45, 0x4c, 0xab, - 0xee, 0x13, 0x8f, 0x2c, 0xdf, 0xc6, 0xef, 0x0f, 0x3d, 0xb1, 0x4b, 0x09, 0xdd, 0x02, 0xe7, 0xfa, - 0xbe, 0x9f, 0x15, 0x66, 0x54, 0x43, 0x3b, 0x75, 0xb9, 0x51, 0xae, 0x3c, 0x19, 0x6d, 0x67, 0x01, - 0x92, 0x00, 0x4d, 0xc1, 0x92, 0x4d, 0x19, 0x50, 0xac, 0x59, 0x76, 0x82, 0xea, 0xd1, 0xc0, 0x87, - 0xac, 0xb1, 0x46, 0x12, 0x47, 0xdf, 0xab, 0x3c, 0xd4, 0x22, 0x79, 0xf9, 0x16, 0x9b, 0x69, 0xb3, - 0x34, 0x35, 0xdb, 0x9d, 0x89, 0x94, 0xac, 0x04, 0xb1, 0x5d, 0x45, 0x00, 0x7f, 0x0f, 0x98, 0x33, - 0x4a, 0xc5, 0xd7, 0x83, 0x21, 0x9b, 0x37, 0x6d, 0x9e, 0xd7, 0x55, 0x3f, 0x52, 0xcb, 0xad, 0xf1, - 0x59, 0xf6, 0x92, 0x82, 0x05, 0x0a, 0x4c, 0x84, 0x34, 0x67, 0xae, 0x7e, 0x4c, 0x4a, 0x14, 0x2a, - 0x61, 0xcc, 0xc4, 0xc4, 0x8d, 0xc3, 0xaa, 0xc6, 0xb8, 0xe0, 0xa8, 0xd6, 0x18, 0xe0, 0x6c, 0x60, - 0xdc, 0x92, 0x4a, 0x94, 0x3a, 0xf9, 0x00, 0x0f, 0x89, 0x64, 0x46, 0xcb, 0x62, 0xef, 0x4e, 0x47, - 0xad, 0x6d, 0x1f, 0xbe, 0x2b, 0x89, 0x84, 0xa4, 0x8a, 0xcd, 0x7c, 0x05, 0x19, 0xd6, 0xe8, 0x7a, - 0x33, 0x10, 0x6e, 0x9a, 0x91, 0xc2, 0x98, 0x80, 0x8b, 0xf0, 0x4e, 0x12, 0x67, 0x34, 0x93, 0x34, - 0xe6, 0xc2, 0xac, 0x01, 0x5a, 0x9f, 0x70, 0x4b, 0x2d, 0xdb, 0x56, 0xd2, 0x7a, 0x8b, 0x00, 0xd8, - 0x76, 0xad, 0x00, 0xf6, 0x24, 0x34, 0x09, 0x5a, 0x6f, 0x7a, 0x5e, 0x9c, 0xd4, 0xb2, 0x7f, 0xe0, - 0x9f, 0xf6, 0x90, 0x19, 0xae, 0x5f, 0x27, 0x33, 0x07, 0x92, 0xcf, 0x9a, 0x1c, 0x07, 0xfb, 0xe9, - 0x8d, 0x23, 0xc4, 0xe6, 0x15, 0x33, 0x2a, 0x77, 0xdb, 0x5b, 0x61, 0x82, 0xd2, 0x77, 0xdd, 0xfd, - 0x75, 0x72, 0xda, 0x75, 0x74, 0x8d, 0xfb, 0x3b, 0x93, 0x15, 0x29, 0x4e, 0xab, 0xe4, 0xb6, 0xd0, - 0xfc, 0x31, 0x14, 0xb2, 0xaf, 0xf5, 0x53, 0x7a, 0x6b, 0x05, 0xa7, 0xaa, 0x9a, 0xbb, 0x49, 0x30, - 0xe1, 0x7b, 0x7f, 0xd3, 0xd8, 0xf8, 0x1a, 0xa2, 0xd6, 0x74, 0xdb, 0x69, 0xad, 0x96, 0x7c, 0x83, - 0x06, 0xfc, 0x3e, 0x1c, 0xa8, 0xbf, 0x78, 0xf4, 0x46, 0xb2, 0x6d, 0x8f, 0x2f, 0xd1, 0xaf, 0x6f, - 0x7f, 0xd7, 0xe5, 0x28, 0x67, 0xbc, 0xcc, 0x3b, 0x4d, 0xbd, 0x72, 0x27, 0xea, 0x99, 0x2d, 0xd9, - 0x95, 0xd7, 0x7c, 0x4a, 0x2b, 0xc8, 0x08, 0xc7, 0xa3, 0xe6, 0x8f, 0xa4, 0x33, 0x67, 0x5f, 0x48, - 0x9b, 0xc5, 0x03, 0xdb, 0x70, 0x11, 0x5a, 0x82, 0x46, 0x69, 0xe4, 0xb1, 0x86, 0x1c, 0xdb, 0xd5, - 0x57, 0xa1, 0xf7, 0x8f, 0x1a, 0x4b, 0x35, 0x82, 0xb2, 0x55, 0x3a, 0xc2, 0x3f, 0x12, 0xa4, 0x32, - 0x6e, 0xf9, 0x43, 0xc5, 0x6f, 0x84, 0x7b, 0x8c, 0x67, 0x96, 0x95, 0x61, 0x4b, 0xbb, 0x1d, 0xde, - 0xa9, 0xcf, 0xb7, 0xd7, 0x99, 0xce, 0x42, 0xdf, 0x8b, 0x73, 0x5f, 0xeb, 0x9d, 0xc1, 0x19, 0xdb, - 0xca, 0x30, 0x6d, 0xa4, 0xfa, 0x84, 0xa5, 0x0f, 0x79, 0x90, 0x5e, 0x5c, 0x2a, 0xc1, 0xf6, 0x12, - 0x41, 0xd8, 0x83, 0x59, 0xee, 0x6f, 0x62, 0xdc, 0x9d, 0x3e, 0x4a, 0x15, 0x93, 0xf4, 0x1c, 0xc5, - 0xf3, 0xc0, 0x22, 0x41, 0xce, 0x5e, 0xb6, 0x2c, 0x28, 0xa5, 0x47, 0xfc, 0xaa, 0xdf, 0x1d, 0xd6, - 0x88, 0xf3, 0xdf, 0xba, 0x87, 0x0d, 0x9b, 0xed, 0x72, 0x3b, 0x02, 0xb0, 0xb1, 0xcd, 0xb4, 0xc0, - 0x8d, 0x09, 0x3c, 0x48, 0x97, 0x7e, 0x06, 0x21, 0x93, 0x0a, 0x66, 0x8a, 0x5c, 0xd5, 0x17, 0xa0, - 0xef, 0xfb, 0xe2, 0x48, 0xb4, 0xaa, 0xcf, 0x30, 0x09, 0xba, 0xc0, 0xd4, 0x7f, 0x49, 0x37, 0x8f, - 0x30, 0xa3, 0xd2, 0xc2, 0x80, 0x0c, 0xd1, 0x6b, 0x3c, 0x94, 0x64, 0x56, 0x3d, 0xa4, 0xa7, 0x32, - 0x10, 0x82, 0xfa, 0xe9, 0x5d, 0xe5, 0x64, 0x5f, 0xcc, 0xbc, 0x55, 0x69, 0xe9, 0x42, 0xb5, 0xf8, - 0x60, 0xa3, 0x2f, 0x04, 0x08, 0x27, 0x23, 0xe9, 0xe5, 0x3f, 0x2d, 0x18, 0x09, 0x9b, 0xec, 0x5e, - 0x23, 0xa9, 0x16, 0x35, 0x41, 0x98, 0x32, 0x5f, 0xa8, 0xda, 0xea, 0xa3, 0xf6, 0xb7, 0xab, 0x6a, - 0xad, 0x6e, 0xf1, 0x2a, 0x03, 0xcb, 0xdc, 0x27, 0xb8, 0x3c, 0x81, 0x8f, 0x44, +static const uint8_t ml_dsa_65_0_sig_gen_msg[] = { + 0x58, 0x70, 0xbb, 0x28, 0x8a, 0xa6, 0x13, 0x07, 0x08, 0xf7, 0xbb, 0xad, 0x9f, 0xbd, 0xd6, 0xd4, + 0x1e, 0x24, 0x9d, 0x62, 0x04, 0x95, 0xac, 0xfe, 0x90, 0xc6, 0x17, 0x37, 0xb5, 0x7d, 0xba, 0x89, + 0x02, 0x13, 0xd4, 0x74, 0x17, 0x18, 0x54, 0x5c, 0xcd, 0x8b, 0x3f, 0xff, 0xc2, 0xdb, 0x33, 0xc3, + 0x9a, 0xd6, 0x31, 0xd5, 0xb5, 0xcc, 0x90, 0x2d, 0xe4, 0xd3, 0x40, 0xdf, 0x03, 0xe0, 0x92, 0x48, + 0xf6, 0x7e, 0x89, 0xd2, 0x80, 0x71, 0xaa, 0x50, 0xfa, 0x53, 0x2e, 0x94, 0xc3, 0x91, 0xd2, 0xd1, + 0xa6, 0x1b, 0x18, 0x47, 0xc6, 0xb1, 0x08, 0x8b, 0xe5, 0x55, 0xe5, 0xc2, 0x69, 0x4e, 0xb0, 0xfc, + 0x1f, 0x02, 0x90, 0x95, 0xac, 0xd9, 0xde, 0xb2, 0x1e, 0xf8, 0x86, 0xbe, 0x57, 0x76, 0x82, 0xca, + 0x96, 0xaa, 0x2e, 0xb3, 0xdc, 0xb2, 0x4b, 0x87, 0x13, 0x36, 0xac, 0x5f, 0x23, 0xc8, 0x48, 0x80, + 0x11, 0x86, 0x0b, 0x45, 0x5b, 0x68, 0x7b, 0xd4, 0xce, 0xf5, 0xfa, 0x11, 0x38, 0x1b, 0xc2, 0x92, + 0xb4, 0x09, 0x8b, 0xb2, 0xcf, 0xc1, 0x82, 0x2b, 0x48, 0xec, 0xfd, 0x28, 0xae, 0xad, 0xa7, 0x18, + 0x09, 0xbf, 0xda, 0x19, 0x08, 0x36, 0xd3, 0x21, 0x5c, 0xfe, 0x75, 0x5f, 0xdd, 0x93, 0x74, 0x11, + 0x5e, 0x5a, 0x0c, 0xca, 0xe1, 0x52, 0x40, 0xeb, 0xa0, 0x14, 0x7c, 0x2f, 0x89, 0xd8, 0xd2, 0x44, + 0x54, 0xd7, 0xa5, 0xac, 0x2d, 0x20, 0xec, 0xc0, 0xd4, 0x6c, 0x04, 0x0f, 0xad, 0x23, 0x3f, 0xc5, + 0x1c, 0x87, 0x00, 0x80, 0xf1, 0xfc, 0xef, 0xae, 0x6c, 0x07, 0x3a, 0xf5, 0xf7, 0xa7, 0x8d, 0x61, + 0x0e, 0x23, 0x83, 0x1d, 0x59, 0x90, 0x98, 0x5f, 0xdb, 0xfd, 0xc6, 0xd1, 0x01, 0xac, 0xf3, 0xdb, + 0x0a, 0x74, 0xd7, 0x17, 0x39, 0xe0, }; -static const uint8_t ml_dsa_65_0_sig_digest[] = { - 0x3a, 0xd5, 0xf2, 0x3a, 0xbe, 0x98, 0x11, 0x09, 0x1f, 0x76, 0xfc, 0xe1, 0xb0, 0x8a, 0x52, 0x04, - 0x9a, 0x4f, 0x4f, 0xc2, 0x5f, 0x72, 0xa1, 0xc3, 0x3f, 0x84, 0xd0, 0x4f, 0xb1, 0x0b, 0xb3, 0x66, +static const uint8_t ml_dsa_65_0_sig_gen_digest[] = { + 0x4b, 0xc8, 0xd8, 0x3f, 0x15, 0xe8, 0x52, 0x6c, 0x22, 0xaf, 0x50, 0x68, 0x60, 0x68, 0x95, 0x86, + 0x5c, 0x70, 0x32, 0xa8, 0x1e, 0xea, 0x6d, 0xc3, 0xa9, 0x7d, 0xf5, 0x48, 0xa7, 0xdf, 0x8a, 0x8f, }; -static const uint8_t ml_dsa_65_1_sig_priv[] = { +static const uint8_t ml_dsa_65_1_sig_gen_priv[] = { 0xf2, 0x6b, 0xfe, 0x12, 0x68, 0x86, 0xf4, 0x82, 0x22, 0x94, 0x4d, 0x02, 0x18, 0xfa, 0xc1, 0x7c, 0xd8, 0xa9, 0xcc, 0x6d, 0x67, 0xa0, 0x23, 0xfd, 0xc0, 0x7a, 0xff, 0xc2, 0xd0, 0x25, 0xf7, 0x70, 0x63, 0x85, 0x0d, 0x88, 0x0e, 0x98, 0xfe, 0xe5, 0x02, 0xe0, 0x17, 0x32, 0x70, 0x00, 0xcc, 0xaf, @@ -965,7 +583,7 @@ static const uint8_t ml_dsa_65_1_sig_priv[] = { 0x3a, 0x8a, 0xf2, 0x85, 0x89, 0xe8, 0xf6, 0x29, 0x48, 0xed, 0xb6, 0xbe, 0x76, 0x64, 0x6d, 0x59, 0x66, 0x06, 0xb9, 0xe7, 0x05, 0xfe, 0xe3, 0xf1, 0x44, 0xa0, 0x7b, 0xc9, 0xed, 0x1d, 0x40, 0x0c, }; -static const uint8_t ml_dsa_65_1_sig_msg[] = { +static const uint8_t ml_dsa_65_1_sig_gen_msg[] = { 0x88, 0x5a, 0x0b, 0xdd, 0x8d, 0xe7, 0x4b, 0xc7, 0x11, 0x69, 0x0a, 0xa6, 0x14, 0xdd, 0xa5, 0x32, 0xf4, 0xd8, 0xc7, 0xea, 0x2c, 0x27, 0x85, 0x5a, 0x57, 0x8e, 0x63, 0x61, 0xca, 0xae, 0x2c, 0x0b, 0xf7, 0xe7, 0x73, 0xb4, 0x90, 0x0a, 0x32, 0x93, 0x12, 0x1a, 0x6e, 0x0d, 0xd6, 0x10, 0x10, 0x7a, @@ -1391,21 +1009,20 @@ static const uint8_t ml_dsa_65_1_sig_msg[] = { 0x60, 0x28, 0x77, 0xee, }; -static const uint8_t ml_dsa_65_1_sig_digest[] = { +static const uint8_t ml_dsa_65_1_sig_gen_digest[] = { 0x04, 0x0d, 0xa6, 0x57, 0x2b, 0x30, 0x8b, 0xbd, 0x33, 0x25, 0x00, 0xfd, 0x67, 0x5f, 0xb8, 0x89, 0x23, 0xfc, 0xd7, 0xa9, 0xca, 0xe0, 0x15, 0x0b, 0xd0, 0x1c, 0x48, 0x48, 0x46, 0x52, 0xcc, 0x66, }; -static const uint8_t ml_dsa_65_1_sig_add_random[] = { +static const uint8_t ml_dsa_65_1_sig_gen_add_random[] = { 0x4e, 0x7a, 0x01, 0x7c, 0x15, 0x03, 0x9d, 0xc2, 0x00, 0x51, 0xd2, 0x96, 0x0e, 0x5e, 0x15, 0x59, 0xcc, 0x27, 0xed, 0x46, 0x87, 0x7c, 0xb9, 0x81, 0x16, 0x19, 0x9a, 0x0f, 0x41, 0x05, 0xfe, 0x32, }; -static ML_DSA_SIG_TEST_DATA slh_dsa_sig_testdata[] = { - ML_DSA_SIG_TEST_DET_ITEM("ML-DSA-65", ml_dsa_65_0), - ML_DSA_SIG_TEST_ITEM("ML-DSA-65", ml_dsa_65_1), +static ML_DSA_SIG_TEST_DATA ml_dsa_siggen_testdata[] = { + ML_DSA_SIG_GEN_TEST_DET_ITEM("ML-DSA-65", ml_dsa_65_0_sig_gen), + ML_DSA_SIG_GEN_TEST_ITEM("ML-DSA-65", ml_dsa_65_1_sig_gen), }; -#endif /* * Test vectors from diff --git a/test/ml_dsa_test.c b/test/ml_dsa_test.c index ede4e9345c2..f6ec12d0498 100644 --- a/test/ml_dsa_test.c +++ b/test/ml_dsa_test.c @@ -46,6 +46,38 @@ err: return pkey; } +static int ml_dsa_create_keypair(EVP_PKEY **pkey, const char *name, + const uint8_t *priv, size_t priv_len, + const uint8_t *pub, size_t pub_len) +{ + int ret = 0, selection = 0; + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM params[3], *p = params; + + if (priv != NULL) { + *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, + (uint8_t *)priv, priv_len); + selection = OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + } + if (pub != NULL) { + *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + (uint8_t *)pub, pub_len); + selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY; + } + *p = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(lib_ctx, name, NULL)) + || !TEST_int_eq(EVP_PKEY_fromdata_init(ctx), 1) + || !TEST_int_eq(EVP_PKEY_fromdata(ctx, pkey, selection, + params), 1)) + goto err; + + ret = 1; +err: + EVP_PKEY_CTX_free(ctx); + return ret; +} + static int ml_dsa_keygen_test(int tst_id) { int ret = 0; @@ -72,6 +104,69 @@ err: return ret; } +static int ml_dsa_siggen_test(int tst_id) +{ + int ret = 0; + ML_DSA_SIG_TEST_DATA *td = &ml_dsa_siggen_testdata[tst_id]; + EVP_PKEY_CTX *sctx = NULL; + EVP_PKEY *pkey = NULL; + EVP_SIGNATURE *sig_alg = NULL; + OSSL_PARAM params[4], *p = params; + uint8_t *psig = NULL; + size_t psig_len = 0, sig_len2 = 0; + uint8_t digest[32]; + size_t digest_len = sizeof(digest); + int encode = 0, deterministic = 1; + + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, &deterministic); + *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, &encode); + if (td->add_random != NULL) + *p++ = OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_TEST_ENTROPY, + (char *)td->add_random, + td->add_random_len); + *p = OSSL_PARAM_construct_end(); + + /* + * This just uses from data here, but keygen also works. + * The keygen path is tested via ml_dsa_keygen_test + */ + if (!ml_dsa_create_keypair(&pkey, td->alg, td->priv, td->priv_len, + NULL, 0)) + goto err; + + if (!TEST_ptr(sctx = EVP_PKEY_CTX_new_from_pkey(lib_ctx, pkey, NULL))) + goto err; + if (!TEST_ptr(sig_alg = EVP_SIGNATURE_fetch(lib_ctx, td->alg, NULL))) + goto err; + if (!TEST_int_eq(EVP_PKEY_sign_message_init(sctx, sig_alg, params), 1) + || !TEST_int_eq(EVP_PKEY_sign(sctx, NULL, &psig_len, + td->msg, td->msg_len), 1) + || !TEST_true(EVP_PKEY_get_size_t_param(pkey, OSSL_PKEY_PARAM_MAX_SIZE, + &sig_len2)) + || !TEST_int_eq(sig_len2, psig_len) + || !TEST_ptr(psig = OPENSSL_zalloc(psig_len)) + || !TEST_int_eq(EVP_PKEY_sign(sctx, psig, &psig_len, + td->msg, td->msg_len), 1)) + goto err; + if (!TEST_int_eq(EVP_Q_digest(lib_ctx, "SHA256", NULL, psig, psig_len, + digest, &digest_len), 1)) + goto err; + if (!TEST_mem_eq(digest, digest_len, td->sig_digest, td->sig_digest_len)) + goto err; + + if (!TEST_int_eq(EVP_PKEY_verify_message_init(sctx, sig_alg, params), 1) + || !TEST_int_eq(EVP_PKEY_verify(sctx, psig, psig_len, + td->msg, td->msg_len), 1)) + goto err; + ret = 1; +err: + EVP_SIGNATURE_free(sig_alg); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(sctx); + OPENSSL_free(psig); + return ret; +} + const OPTIONS *test_get_options(void) { static const OPTIONS options[] = { @@ -104,6 +199,7 @@ int setup_tests(void) return 0; ADD_ALL_TESTS(ml_dsa_keygen_test, OSSL_NELEM(ml_dsa_keygen_testdata)); + ADD_ALL_TESTS(ml_dsa_siggen_test, OSSL_NELEM(ml_dsa_siggen_testdata)); return 1; } diff --git a/util/perl/OpenSSL/paramnames.pm b/util/perl/OpenSSL/paramnames.pm index 9fc6c5126e4..c2eb63ee2b7 100644 --- a/util/perl/OpenSSL/paramnames.pm +++ b/util/perl/OpenSSL/paramnames.pm @@ -469,6 +469,9 @@ my %params = ( 'SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK' => "sign-x931-pad-check", 'SIGNATURE_PARAM_FIPS_APPROVED_INDICATOR' => '*ALG_PARAM_FIPS_APPROVED_INDICATOR', 'SIGNATURE_PARAM_SIGNATURE' => "signature", + 'SIGNATURE_PARAM_MESSAGE_ENCODING' => "message-encoding", + 'SIGNATURE_PARAM_DETERMINISTIC' => "deterministic", + 'SIGNATURE_PARAM_TEST_ENTROPY' => "test-entropy", # Asym cipher parameters 'ASYM_CIPHER_PARAM_DIGEST' => '*PKEY_PARAM_DIGEST',