]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add ML-DSA sign/verify
authorslontis <shane.lontis@oracle.com>
Fri, 20 Dec 2024 03:18:27 +0000 (14:18 +1100)
committerTomas Mraz <tomas@openssl.org>
Fri, 14 Feb 2025 09:46:03 +0000 (10:46 +0100)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26127)

22 files changed:
crypto/ml_dsa/build.info
crypto/ml_dsa/ml_dsa_encoders.c
crypto/ml_dsa/ml_dsa_hash.h [new file with mode: 0644]
crypto/ml_dsa/ml_dsa_key.c
crypto/ml_dsa/ml_dsa_key_compress.c
crypto/ml_dsa/ml_dsa_local.h
crypto/ml_dsa/ml_dsa_matrix.h
crypto/ml_dsa/ml_dsa_params.c
crypto/ml_dsa/ml_dsa_params.h
crypto/ml_dsa/ml_dsa_poly.h
crypto/ml_dsa/ml_dsa_sample.c
crypto/ml_dsa/ml_dsa_sign.c
crypto/ml_dsa/ml_dsa_sign.h [new file with mode: 0644]
crypto/ml_dsa/ml_dsa_vector.h
include/crypto/ml_dsa.h
providers/defltprov.c
providers/implementations/include/prov/implementations.h
providers/implementations/signature/build.info
providers/implementations/signature/ml_dsa_sig.c [new file with mode: 0644]
test/ml_dsa.inc
test/ml_dsa_test.c
util/perl/OpenSSL/paramnames.pm

index f972fed51c68246311a384287c6b4f4949965dd7..757d3b8d3e44e422e1b6648e0b256ea2799f926b 100644 (file)
@@ -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
index 844a1e979289579caf71a2b4ecfabc12283fc7f2..c72091bfbbec134bbae5cb10253960526047a7bb 100644 (file)
  * https://www.openssl.org/source/license.html
  */
 
+#include <assert.h>
 #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 (file)
index 0000000..532d9e5
--- /dev/null
@@ -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 <openssl/evp.h>
+
+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);
+}
index f8a488f0d1d8aa6a872d5c6957662b96eee21e2e..57eb599507fe276caee50a42d429e092dde2cf6f 100644 (file)
@@ -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
 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 + kk);
+    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 */
index 035475b885ab663c67532f4dab1b143d2846d084..ffeb2b43e45bf2e7aed5ed40192c15f29104eeee 100644 (file)
@@ -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;
index 54e1d3b35a7526b7877d7bed27cb109dbfee2e20..ea9d92348e884ce89d6673fbfcda293fa3ac9e5f 100644 (file)
 
 # 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 */
index 2a79c6c59dd8fa72ffe729003b7c97d1e91bbd47..afbe6e31df1c67203efdb0106097d1bf2be716ae 100644 (file)
@@ -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);
+}
index 63b50c6d2cf09af184a5bd53443862e5f31d2d54..e1a86943f99c95873d6cbe0723f05d2e721372b4 100644 (file)
 #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},
 };
 
index 1645735ef00e924121545aa809432470497474c6..e6f4725aac9428b8f2d354a370b6ef3c1f8eb723 100644 (file)
@@ -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 */
index 734bfb9b1e43083c9e32a2b341ffda68c9aded55..9c4696499ca3fd9b87e4e1dcf72f74f7ce27b067 100644 (file)
@@ -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
index 52015c16b7ec33054c566a4701b8d54ce7dbc7a5..0e7e5ef45ea31a6bc1feb55d7433b2a22233948f 100644 (file)
@@ -7,13 +7,15 @@
  * https://www.openssl.org/source/license.html
  */
 
-#include <openssl/evp.h>
 #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;
+}
index adb9435a318ef5431465b2688b53564a245a0e9b..f7abc1bad01abcbef38c3d766a0b9397f46c62fa 100644 (file)
 #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 <int K, int L>
-static int ossl_ml_dsa_sign_internal(
-    uint8_t out_encoded_signature[signature_bytes<K>()],
-    const struct private_key<K, L> *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<K, L> sign;
-    vector<L> s1_ntt;
-    vector<K> s2_ntt;
-    vector<K> t0_ntt;
-    matrix<K, L> a_ntt;
-    vector<L> y;
-    vector<K> w;
-    vector<K> w1;
-    vector<L> cs1;
-    vector<K> cs2;
-  };
-  std::unique_ptr<values_st, DeleterFree<values_st>> values(
-      reinterpret_cast<struct values_st *>(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<L> *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<K>());
-
-    scalar c_ntt;
-    scalar_sample_in_ball_vartime(&c_ntt, values->sign.c_tilde,
-                                  sizeof(values->sign.c_tilde), tau<K>());
-    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<K> *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<K>() - beta<K>()) |
-            constant_time_ge_w(r0_max, kGamma2 - beta<K>()))) {
-      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<K> *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<K>(), 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<K>());
-    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<K>());
-    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 (file)
index 0000000..6167da6
--- /dev/null
@@ -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;
+};
index 5aa84511c2e3287927255927d1f29380f912dfe0..64f4efde0485514e528ed45c8f971294f1731ce5 100644 (file)
@@ -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
index 124e65441d02e262bcfa43641376c93fb3e7eeff..e93539670edb0304400b36ddfe52177eaf4de66f 100644 (file)
@@ -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 */
index 486186413060a27de9cdd29103de1e94445e4646..2b8b8c5c00ee68b4cc9a94d0052bb31674422c4b 100644 (file)
@@ -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",
index cfafbf48873fda91189e17cbacc3241d6a8ad78f..f1985805a26dfa4a4d5c24fa055d36a8800d8f7b 100644 (file)
@@ -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[];
index 6987a12250593632a9cdfb2e3d4c5963a0f93813..a37fc68a66d74d87a4feb4b29ba817e4d1399397 100644 (file)
@@ -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 (file)
index 0000000..d6eadcb
--- /dev/null
@@ -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 <assert.h>
+#include <string.h> /* memset */
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#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);
index 273ac7f67416aebe49723f8d9c6225d633f492f6..d407d76a6d023ea173c95eacb1fb88e1016beebf 100644 (file)
@@ -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
index ede4e9345c2f12451b869dceba0b80a6a39e2c31..f6ec12d04982fb79039f24b7ed4b36393b28c93e 100644 (file)
@@ -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;
 }
 
index 9fc6c5126e461358d9c503a1f50b0cb4253eec1f..c2eb63ee2b7f317dd12f4d64016dccdc960191aa 100644 (file)
@@ -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',