From b6a0a518a3ee815cf64443c91b52a5b6b26ebcb0 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Tue, 21 Jun 2016 10:33:21 -0400 Subject: [PATCH 01/40] Simplify version configuration. OpenSSL's SSL_OP_NO_* flags allow discontinuous version ranges. This is a nuisance for two reasons. First it makes it unnecessarily difficult to answer "are any versions below TLS 1.3 enabled?". Second the protocol does not allow discontinuous version ranges on the client anyway. OpenSSL instead picks the first continous range of enabled versions on the client, but not the server. This is bizarrely inconsistent. It also doesn't quite do this as the ClientHello sending logic does this, but not the ServerHello processing logic. So we actually break some invariants slightly. The logic is also cumbersome in DTLS which kindly inverts the comparison logic. First, switch min_version/max_version's storage to normalized versions. Next replace all the ad-hoc version-related functions with a single ssl_get_version_range function. Client and server now consistently pick a contiguous range of versions. Note this is a slight behavior change for servers. Version-range-sensitive logic is rewritten to use this new function. BUG=66 Change-Id: Iad0d64f2b7a917603fc7da54c9fc6656c5fbdb24 Reviewed-on: https://boringssl-review.googlesource.com/8513 Reviewed-by: David Benjamin --- include/openssl/ssl.h | 12 +- ssl/d1_meth.c | 33 +++++ ssl/handshake_client.c | 45 ++++--- ssl/handshake_server.c | 30 +++-- ssl/internal.h | 49 ++++---- ssl/s3_meth.c | 10 ++ ssl/ssl_lib.c | 277 +++++++++++------------------------------ ssl/ssl_test.cc | 13 +- ssl/t1_lib.c | 2 +- 9 files changed, 201 insertions(+), 270 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 797da3895..59096e4c4 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -3626,10 +3626,12 @@ struct ssl_ctx_st { /* lock is used to protect various operations on this object. */ CRYPTO_MUTEX lock; - /* max_version is the maximum acceptable wire protocol version. */ + /* max_version is the maximum acceptable protocol version. Note this version + * is normalized in DTLS. */ uint16_t max_version; - /* min_version is the minimum acceptable wire protocol version. */ + /* min_version is the minimum acceptable protocol version. Note this version + * is normalized in DTLS. */ uint16_t min_version; struct ssl_cipher_preference_list_st *cipher_list; @@ -3873,10 +3875,12 @@ struct ssl_st { /* version is the protocol version. */ int version; - /* max_version is the maximum acceptable wire protocol version. */ + /* max_version is the maximum acceptable protocol version. Note this version + * is normalized in DTLS. */ uint16_t max_version; - /* min_version is the minimum acceptable wire protocol version. */ + /* min_version is the minimum acceptable protocol version. Note this version + * is normalized in DTLS. */ uint16_t min_version; /* method is the method table corresponding to the current protocol (DTLS or diff --git a/ssl/d1_meth.c b/ssl/d1_meth.c index 78119a028..19b7af06b 100644 --- a/ssl/d1_meth.c +++ b/ssl/d1_meth.c @@ -56,11 +56,44 @@ #include +#include + #include "internal.h" +static uint16_t dtls1_version_from_wire(uint16_t wire_version) { + uint16_t tls_version = ~wire_version; + uint16_t version = tls_version + 0x0201; + /* If either component overflowed, clamp it so comparisons still work. */ + if ((version >> 8) < (tls_version >> 8)) { + version = 0xff00 | (version & 0xff); + } + if ((version & 0xff) < (tls_version & 0xff)) { + version = (version & 0xff00) | 0xff; + } + /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */ + if (version == TLS1_VERSION) { + version = TLS1_1_VERSION; + } + return version; +} + +static uint16_t dtls1_version_to_wire(uint16_t version) { + assert(version >= TLS1_1_VERSION); + + /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */ + if (version == TLS1_1_VERSION) { + return DTLS1_VERSION; + } + return ~(version - 0x0201); +} + static const SSL_PROTOCOL_METHOD DTLS_protocol_method = { 1 /* is_dtls */, + TLS1_1_VERSION, + TLS1_2_VERSION, + dtls1_version_from_wire, + dtls1_version_to_wire, dtls1_new, dtls1_free, dtls1_get_message, diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index a55327bf6..b625f37f4 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -573,7 +573,7 @@ static int ssl3_write_client_cipher_list(SSL *ssl, CBB *out) { /* TODO(davidben): Also check |SSL_CIPHER_get_max_version| against the * minimum enabled version. See https://crbug.com/boringssl/66. */ if (SSL_CIPHER_get_min_version(cipher) > - ssl3_version_from_wire(ssl, ssl->client_version)) { + ssl->method->version_from_wire(ssl->client_version)) { continue; } any_enabled = 1; @@ -622,30 +622,31 @@ static int ssl3_send_client_hello(SSL *ssl) { CBB cbb; CBB_zero(&cbb); + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + goto err; + } + assert(ssl->state == SSL3_ST_CW_CLNT_HELLO_A); if (!ssl->s3->have_version) { - uint16_t max_version = ssl3_get_max_client_version(ssl); - /* Disabling all versions is silly: return an error. */ - if (max_version == 0) { - OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); - goto err; - } - - ssl->version = max_version; + ssl->version = ssl->method->version_to_wire(max_version); /* Only set |ssl->client_version| on the initial handshake. Renegotiations, * although locked to a version, reuse the value. When using the plain RSA * key exchange, the ClientHello version is checked in the premaster secret. * Some servers fail when this value changes. */ - ssl->client_version = max_version; + ssl->client_version = ssl->version; } /* If the configured session has expired or was created at a disabled * version, drop it. */ - if (ssl->session != NULL && - (ssl->session->session_id_length == 0 || ssl->session->not_resumable || - ssl->session->timeout < (long)(time(NULL) - ssl->session->time) || - !ssl3_is_version_enabled(ssl, ssl->session->ssl_version))) { - SSL_set_session(ssl, NULL); + if (ssl->session != NULL) { + uint16_t session_version = + ssl->method->version_from_wire(ssl->session->ssl_version); + if (ssl->session->session_id_length == 0 || ssl->session->not_resumable || + ssl->session->timeout < (long)(time(NULL) - ssl->session->time) || + session_version < min_version || session_version > max_version) { + SSL_set_session(ssl, NULL); + } } /* If resending the ClientHello in DTLS after a HelloVerifyRequest, don't @@ -747,7 +748,7 @@ static int ssl3_get_server_hello(SSL *ssl) { int al = SSL_AD_INTERNAL_ERROR, ok; long n; CBS server_hello, server_random, session_id; - uint16_t server_version, cipher_suite; + uint16_t server_wire_version, server_version, cipher_suite; uint8_t compression_method; n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO, ssl_hash_message, @@ -770,7 +771,7 @@ static int ssl3_get_server_hello(SSL *ssl) { CBS_init(&server_hello, ssl->init_msg, n); - if (!CBS_get_u16(&server_hello, &server_version) || + if (!CBS_get_u16(&server_hello, &server_wire_version) || !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) || !CBS_get_u8_length_prefixed(&server_hello, &session_id) || CBS_len(&session_id) > SSL3_SESSION_ID_SIZE || @@ -781,20 +782,24 @@ static int ssl3_get_server_hello(SSL *ssl) { goto f_err; } + server_version = ssl->method->version_from_wire(server_wire_version); + assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete); if (!ssl->s3->have_version) { - if (!ssl3_is_version_enabled(ssl, server_version)) { + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version) || + server_version < min_version || server_version > max_version) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); al = SSL_AD_PROTOCOL_VERSION; goto f_err; } - ssl->version = server_version; + ssl->version = server_wire_version; ssl->s3->enc_method = ssl3_get_enc_method(server_version); assert(ssl->s3->enc_method != NULL); /* At this point, the connection's version is known and ssl->version is * fixed. Begin enforcing the record-layer version. */ ssl->s3->have_version = 1; - } else if (server_version != ssl->version) { + } else if (server_wire_version != ssl->version) { OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); al = SSL_AD_PROTOCOL_VERSION; goto f_err; diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index e2838bfd3..133278e78 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c @@ -719,7 +719,7 @@ static int ssl3_get_client_hello(SSL *ssl) { STACK_OF(SSL_CIPHER) *ciphers = NULL; struct ssl_early_callback_ctx early_ctx; CBS client_hello; - uint16_t client_version; + uint16_t client_wire_version; CBS client_random, session_id, cipher_suites, compression_methods; SSL_SESSION *session = NULL; @@ -782,7 +782,7 @@ static int ssl3_get_client_hello(SSL *ssl) { } CBS_init(&client_hello, ssl->init_msg, n); - if (!CBS_get_u16(&client_hello, &client_version) || + if (!CBS_get_u16(&client_hello, &client_wire_version) || !CBS_get_bytes(&client_hello, &client_random, SSL3_RANDOM_SIZE) || !CBS_get_u8_length_prefixed(&client_hello, &session_id) || CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) { @@ -791,9 +791,11 @@ static int ssl3_get_client_hello(SSL *ssl) { goto f_err; } + uint16_t client_version = ssl->method->version_from_wire(client_wire_version); + /* use version from inside client hello, not from record header (may differ: * see RFC 2246, Appendix E, second paragraph) */ - ssl->client_version = client_version; + ssl->client_version = client_wire_version; /* Load the client random. */ memcpy(ssl->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE); @@ -809,27 +811,35 @@ static int ssl3_get_client_hello(SSL *ssl) { } } + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + /* Note: This codepath may run twice if |ssl_get_prev_session| completes * asynchronously. * * TODO(davidben): Clean up the order of events around ClientHello * processing. */ if (!ssl->s3->have_version) { - /* Select version to use */ - uint16_t version = ssl3_get_mutual_version(ssl, client_version); - if (version == 0) { + /* Select the version to use. */ + uint16_t version = client_version; + if (version > max_version) { + version = max_version; + } + if (version < min_version) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); al = SSL_AD_PROTOCOL_VERSION; goto f_err; } - ssl->version = version; + ssl->version = ssl->method->version_to_wire(version); ssl->s3->enc_method = ssl3_get_enc_method(version); assert(ssl->s3->enc_method != NULL); /* At this point, the connection's version is known and |ssl->version| is * fixed. Begin enforcing the record-layer version. */ ssl->s3->have_version = 1; - } else if (SSL_IS_DTLS(ssl) ? (ssl->client_version > ssl->version) - : (ssl->client_version < ssl->version)) { + } else if (client_version < ssl3_protocol_version(ssl)) { OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER); al = SSL_AD_PROTOCOL_VERSION; goto f_err; @@ -916,7 +926,7 @@ static int ssl3_get_client_hello(SSL *ssl) { goto f_err; } - ciphers = ssl_bytes_to_cipher_list(ssl, &cipher_suites); + ciphers = ssl_bytes_to_cipher_list(ssl, &cipher_suites, max_version); if (ciphers == NULL) { goto err; } diff --git a/ssl/internal.h b/ssl/internal.h index 82ca4598f..c98172be2 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -809,6 +809,21 @@ struct ssl_method_st { struct ssl_protocol_method_st { /* is_dtls is one if the protocol is DTLS and zero otherwise. */ char is_dtls; + /* min_version is the minimum implemented version. */ + uint16_t min_version; + /* max_version is the maximum implemented version. */ + uint16_t max_version; + /* version_from_wire maps |wire_version| to a protocol version. For + * SSLv3/TLS, the version is returned as-is. For DTLS, the corresponding TLS + * version is used. Note that this mapping is not injective but preserves + * comparisons. + * + * TODO(davidben): To normalize some DTLS-specific code, move away from using + * the wire version except at API boundaries. */ + uint16_t (*version_from_wire)(uint16_t wire_version); + /* version_to_wire maps |version| to the wire representation. It is an error + * to call it with an invalid version. */ + uint16_t (*version_to_wire)(uint16_t version); int (*ssl_new)(SSL *ssl); void (*ssl_free)(SSL *ssl); long (*ssl_get_message)(SSL *ssl, int msg_type, @@ -954,7 +969,8 @@ enum ssl_session_result_t ssl_get_prev_session( SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket, const struct ssl_early_callback_ctx *ctx); -STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs); +STACK_OF(SSL_CIPHER) * + ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs, uint16_t max_version); void ssl_cipher_preference_list_free( struct ssl_cipher_preference_list_st *cipher_list); struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl); @@ -1167,33 +1183,10 @@ int ssl3_can_false_start(const SSL *ssl); * |version|. */ const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version); -/* ssl3_get_max_server_version returns the maximum SSL/TLS version number - * supported by |ssl| as a server, or zero if all versions are disabled. */ -uint16_t ssl3_get_max_server_version(const SSL *ssl); - -/* ssl3_get_mutual_version selects the protocol version on |ssl| for a client - * which advertises |client_version|. If no suitable version exists, it returns - * zero. */ -uint16_t ssl3_get_mutual_version(SSL *ssl, uint16_t client_version); - -/* ssl3_get_max_client_version returns the maximum protocol version configured - * for the client. It is guaranteed that the set of allowed versions at or below - * this maximum version is contiguous. If all versions are disabled, it returns - * zero. */ -uint16_t ssl3_get_max_client_version(SSL *ssl); - -/* ssl3_is_version_enabled returns one if |version| is an enabled protocol - * version for |ssl| and zero otherwise. */ -int ssl3_is_version_enabled(SSL *ssl, uint16_t version); - -/* ssl3_version_from_wire maps |wire_version| to a protocol version. For - * SSLv3/TLS, the version is returned as-is. For DTLS, the corresponding TLS - * version is used. Note that this mapping is not injective but preserves - * comparisons. - * - * TODO(davidben): To normalize some DTLS-specific code, move away from using - * the wire version except at API boundaries. */ -uint16_t ssl3_version_from_wire(const SSL *ssl, uint16_t wire_version); +/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the + * minimum and maximum enabled protocol versions, respectively. */ +int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version, + uint16_t *out_max_version); /* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to * call this function before the version is determined. */ diff --git a/ssl/s3_meth.c b/ssl/s3_meth.c index 715966b7f..b3cfd8c1b 100644 --- a/ssl/s3_meth.c +++ b/ssl/s3_meth.c @@ -59,8 +59,18 @@ #include "internal.h" +static uint16_t ssl3_version_from_wire(uint16_t wire_version) { + return wire_version; +} + +static uint16_t ssl3_version_to_wire(uint16_t version) { return version; } + static const SSL_PROTOCOL_METHOD TLS_protocol_method = { 0 /* is_dtls */, + SSL3_VERSION, + TLS1_3_VERSION, + ssl3_version_from_wire, + ssl3_version_to_wire, ssl3_new, ssl3_free, ssl3_get_message, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index ebac0f4fa..4f90c4732 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -292,19 +292,17 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) { ret->options |= SSL_OP_NO_TICKET; } + ret->min_version = ret->method->min_version; + ret->max_version = ret->method->max_version; + /* Lock the SSL_CTX to the specified version, for compatibility with legacy * uses of SSL_METHOD. */ if (method->version != 0) { SSL_CTX_set_max_version(ret, method->version); SSL_CTX_set_min_version(ret, method->version); - } else if (method->method->is_dtls) { - /* TODO(svaldez): Enable DTLS 1.3 once implemented. */ - SSL_CTX_set_max_version(ret, DTLS1_2_VERSION); - SSL_CTX_set_min_version(ret, DTLS1_VERSION); - } else { - /* TODO(svaldez): Enable TLS 1.3 once implemented. */ + } else if (!method->method->is_dtls) { + /* TODO(svaldez): Enable TLS 1.3 by default once fully implemented. */ SSL_CTX_set_max_version(ret, TLS1_2_VERSION); - SSL_CTX_set_min_version(ret, SSL3_VERSION); } return ret; @@ -842,19 +840,19 @@ int SSL_get_error(const SSL *ssl, int ret_code) { } void SSL_CTX_set_min_version(SSL_CTX *ctx, uint16_t version) { - ctx->min_version = version; + ctx->min_version = ctx->method->version_from_wire(version); } void SSL_CTX_set_max_version(SSL_CTX *ctx, uint16_t version) { - ctx->max_version = version; + ctx->max_version = ctx->method->version_from_wire(version); } void SSL_set_min_version(SSL *ssl, uint16_t version) { - ssl->min_version = version; + ssl->min_version = ssl->method->version_from_wire(version); } void SSL_set_max_version(SSL *ssl, uint16_t version) { - ssl->max_version = version; + ssl->max_version = ssl->method->version_from_wire(version); } uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) { @@ -1494,7 +1492,8 @@ int SSL_set_cipher_list(SSL *ssl, const char *str) { return 1; } -STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs) { +STACK_OF(SSL_CIPHER) * + ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs, uint16_t max_version) { CBS cipher_suites = *cbs; const SSL_CIPHER *c; STACK_OF(SSL_CIPHER) *sk; @@ -1536,9 +1535,7 @@ STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs) { /* Check for FALLBACK_SCSV. */ if (ssl->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff)) { - uint16_t max_version = ssl3_get_max_server_version(ssl); - if (SSL_IS_DTLS(ssl) ? (uint16_t)ssl->version > max_version - : (uint16_t)ssl->version < max_version) { + if (ssl3_protocol_version(ssl) < max_version) { OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK); ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK); goto err; @@ -2515,8 +2512,6 @@ const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) { case TLS1_1_VERSION: case TLS1_2_VERSION: case TLS1_3_VERSION: - case DTLS1_VERSION: - case DTLS1_2_VERSION: return &TLSv1_enc_data; default: @@ -2524,96 +2519,41 @@ const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) { } } -uint16_t ssl3_get_max_server_version(const SSL *ssl) { - uint16_t max_version; +const struct { + uint16_t version; + uint32_t flag; +} kVersions[] = { + {SSL3_VERSION, SSL_OP_NO_SSLv3}, + {TLS1_VERSION, SSL_OP_NO_TLSv1}, + {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1}, + {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2}, + {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3}, +}; - if (SSL_IS_DTLS(ssl)) { - max_version = (ssl->max_version != 0) ? ssl->max_version : DTLS1_2_VERSION; - if (!(ssl->options & SSL_OP_NO_DTLSv1_2) && - DTLS1_2_VERSION >= max_version) { - return DTLS1_2_VERSION; - } - if (!(ssl->options & SSL_OP_NO_DTLSv1) && DTLS1_VERSION >= max_version) { - return DTLS1_VERSION; - } - return 0; - } +static const size_t kVersionsLen = sizeof(kVersions) / sizeof(kVersions[0]); - max_version = (ssl->max_version != 0) ? ssl->max_version : TLS1_3_VERSION; - if (!(ssl->options & SSL_OP_NO_TLSv1_3) && TLS1_3_VERSION <= max_version) { - return TLS1_3_VERSION; - } - if (!(ssl->options & SSL_OP_NO_TLSv1_2) && TLS1_2_VERSION <= max_version) { - return TLS1_2_VERSION; - } - if (!(ssl->options & SSL_OP_NO_TLSv1_1) && TLS1_1_VERSION <= max_version) { - return TLS1_1_VERSION; - } - if (!(ssl->options & SSL_OP_NO_TLSv1) && TLS1_VERSION <= max_version) { - return TLS1_VERSION; - } - if (!(ssl->options & SSL_OP_NO_SSLv3) && SSL3_VERSION <= max_version) { - return SSL3_VERSION; - } - return 0; -} - -uint16_t ssl3_get_mutual_version(SSL *ssl, uint16_t client_version) { - uint16_t version = 0; - - if (SSL_IS_DTLS(ssl)) { - /* Clamp client_version to max_version. */ - if (client_version < ssl->max_version) { - client_version = ssl->max_version; - } - - if (client_version <= DTLS1_2_VERSION && - !(ssl->options & SSL_OP_NO_DTLSv1_2)) { - version = DTLS1_2_VERSION; - } else if (client_version <= DTLS1_VERSION && - !(ssl->options & SSL_OP_NO_DTLSv1)) { - version = DTLS1_VERSION; - } - - /* Check against min_version. */ - if (version != 0 && version > ssl->min_version) { - return 0; - } - return version; - } else { - /* Clamp client_version to max_version. */ - if (client_version > ssl->max_version) { - client_version = ssl->max_version; - } - - if (client_version >= TLS1_3_VERSION && - !(ssl->options & SSL_OP_NO_TLSv1_3)) { - version = TLS1_3_VERSION; - } else if (client_version >= TLS1_2_VERSION && - !(ssl->options & SSL_OP_NO_TLSv1_2)) { - version = TLS1_2_VERSION; - } else if (client_version >= TLS1_1_VERSION && - !(ssl->options & SSL_OP_NO_TLSv1_1)) { - version = TLS1_1_VERSION; - } else if (client_version >= TLS1_VERSION && - !(ssl->options & SSL_OP_NO_TLSv1)) { - version = TLS1_VERSION; - } else if (client_version >= SSL3_VERSION && - !(ssl->options & SSL_OP_NO_SSLv3)) { - version = SSL3_VERSION; - } - - /* Check against min_version. */ - if (version != 0 && version < ssl->min_version) { - return 0; - } - return version; - } -} - -uint16_t ssl3_get_max_client_version(SSL *ssl) { +int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version, + uint16_t *out_max_version) { + /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but + * DTLS 1.0 should be mapped to TLS 1.1. */ uint32_t options = ssl->options; - uint16_t version = 0; + if (SSL_IS_DTLS(ssl)) { + options &= ~SSL_OP_NO_TLSv1_1; + if (options & SSL_OP_NO_DTLSv1) { + options |= SSL_OP_NO_TLSv1_1; + } + } + + uint16_t min_version = ssl->min_version; + uint16_t max_version = ssl->max_version; + + /* Bound the range to only those implemented in this protocol. */ + if (min_version < ssl->method->min_version) { + min_version = ssl->method->min_version; + } + if (max_version > ssl->method->max_version) { + max_version = ssl->method->max_version; + } /* OpenSSL's API for controlling versions entails blacklisting individual * protocols. This has two problems. First, on the client, the protocol can @@ -2624,117 +2564,48 @@ uint16_t ssl3_get_max_client_version(SSL *ssl) { * To account for both of these, OpenSSL interprets the client-side bitmask * as a min/max range by picking the lowest contiguous non-empty range of * enabled protocols. Note that this means it is impossible to set a maximum - * version of TLS 1.2 in a future-proof way. - * - * By this scheme, the maximum version is the lowest version V such that V is - * enabled and V+1 is disabled or unimplemented. */ - if (SSL_IS_DTLS(ssl)) { - if (!(options & SSL_OP_NO_DTLSv1_2)) { - version = DTLS1_2_VERSION; + * version of the higest supported TLS version in a future-proof way. */ + size_t i; + int any_enabled = 0; + for (i = 0; i < kVersionsLen; i++) { + /* Only look at the versions already enabled. */ + if (min_version > kVersions[i].version) { + continue; } - if (!(options & SSL_OP_NO_DTLSv1) && (options & SSL_OP_NO_DTLSv1_2)) { - version = DTLS1_VERSION; + if (max_version < kVersions[i].version) { + break; } - if (version != 0 && version < ssl->max_version) { - version = ssl->max_version; + + if (!(options & kVersions[i].flag)) { + /* The minimum version is the first enabled version. */ + if (!any_enabled) { + any_enabled = 1; + min_version = kVersions[i].version; + } + continue; } - } else { - if (!(options & SSL_OP_NO_TLSv1_3)) { - version = TLS1_3_VERSION; - } - if (!(options & SSL_OP_NO_TLSv1_2) && (options & SSL_OP_NO_TLSv1_3)) { - version = TLS1_2_VERSION; - } - if (!(options & SSL_OP_NO_TLSv1_1) && (options & SSL_OP_NO_TLSv1_2)) { - version = TLS1_1_VERSION; - } - if (!(options & SSL_OP_NO_TLSv1) && (options & SSL_OP_NO_TLSv1_1)) { - version = TLS1_VERSION; - } - if (!(options & SSL_OP_NO_SSLv3) && (options & SSL_OP_NO_TLSv1)) { - version = SSL3_VERSION; - } - if (version != 0 && version > ssl->max_version) { - version = ssl->max_version; + + /* If there is a disabled version after the first enabled one, all versions + * after it are implicitly disabled. */ + if (any_enabled) { + max_version = kVersions[i-1].version; + break; } } - return version; -} - -int ssl3_is_version_enabled(SSL *ssl, uint16_t version) { - if (SSL_IS_DTLS(ssl)) { - if (version < ssl->max_version) { - return 0; - } - if (version > ssl->min_version) { - return 0; - } - - switch (version) { - case DTLS1_VERSION: - return !(ssl->options & SSL_OP_NO_DTLSv1); - - case DTLS1_2_VERSION: - return !(ssl->options & SSL_OP_NO_DTLSv1_2); - - default: - return 0; - } - } else { - if (version > ssl->max_version) { - return 0; - } - if (version < ssl->min_version) { - return 0; - } - - switch (version) { - case SSL3_VERSION: - return !(ssl->options & SSL_OP_NO_SSLv3); - - case TLS1_VERSION: - return !(ssl->options & SSL_OP_NO_TLSv1); - - case TLS1_1_VERSION: - return !(ssl->options & SSL_OP_NO_TLSv1_1); - - case TLS1_2_VERSION: - return !(ssl->options & SSL_OP_NO_TLSv1_2); - - case TLS1_3_VERSION: - return !(ssl->options & SSL_OP_NO_TLSv1_3); - - default: - return 0; - } - } -} - -uint16_t ssl3_version_from_wire(const SSL *ssl, uint16_t wire_version) { - if (!SSL_IS_DTLS(ssl)) { - return wire_version; + if (!any_enabled) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); + return 0; } - uint16_t tls_version = ~wire_version; - uint16_t version = tls_version + 0x0201; - /* If either component overflowed, clamp it so comparisons still work. */ - if ((version >> 8) < (tls_version >> 8)) { - version = 0xff00 | (version & 0xff); - } - if ((version & 0xff) < (tls_version & 0xff)) { - version = (version & 0xff00) | 0xff; - } - /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */ - if (version == TLS1_VERSION) { - version = TLS1_1_VERSION; - } - return version; + *out_min_version = min_version; + *out_max_version = max_version; + return 1; } uint16_t ssl3_protocol_version(const SSL *ssl) { assert(ssl->s3->have_version); - return ssl3_version_from_wire(ssl, ssl->version); + return ssl->method->version_from_wire(ssl->version); } int SSL_is_server(SSL *ssl) { return ssl->server; } diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index b6d4fd6e2..be3b8b7cc 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -695,7 +695,12 @@ static bool TestDefaultVersion(uint16_t min_version, uint16_t max_version, if (!ctx) { return false; } - return ctx->min_version == min_version && ctx->max_version == max_version; + if (ctx->min_version != min_version || ctx->max_version != max_version) { + fprintf(stderr, "Got min %04x, max %04x; wanted min %04x, max %04x\n", + ctx->min_version, ctx->max_version, min_version, max_version); + return false; + } + return true; } static bool CipherGetRFCName(std::string *out, uint16_t value) { @@ -1364,9 +1369,9 @@ int main() { !TestDefaultVersion(TLS1_VERSION, TLS1_VERSION, &TLSv1_method) || !TestDefaultVersion(TLS1_1_VERSION, TLS1_1_VERSION, &TLSv1_1_method) || !TestDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &TLSv1_2_method) || - !TestDefaultVersion(DTLS1_VERSION, DTLS1_2_VERSION, &DTLS_method) || - !TestDefaultVersion(DTLS1_VERSION, DTLS1_VERSION, &DTLSv1_method) || - !TestDefaultVersion(DTLS1_2_VERSION, DTLS1_2_VERSION, &DTLSv1_2_method) || + !TestDefaultVersion(TLS1_1_VERSION, TLS1_2_VERSION, &DTLS_method) || + !TestDefaultVersion(TLS1_1_VERSION, TLS1_1_VERSION, &DTLSv1_method) || + !TestDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &DTLSv1_2_method) || !TestCipherGetRFCName() || !TestPaddingExtension() || !TestClientCAList() || diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index e17d465f9..e733b489d 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1034,7 +1034,7 @@ static int ext_ticket_add_serverhello(SSL *ssl, CBB *out) { * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ static int ext_sigalgs_add_clienthello(SSL *ssl, CBB *out) { - if (ssl3_version_from_wire(ssl, ssl->client_version) < TLS1_2_VERSION) { + if (ssl->method->version_from_wire(ssl->client_version) < TLS1_2_VERSION) { return 1; } From 929d4ee84974ed12dfe16bb7138443eba16c3321 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 24 Jun 2016 23:55:58 -0400 Subject: [PATCH 02/40] Don't send legacy ciphers if min_version >= TLS 1.3. QUIC, in particular, will set min_version to TLS 1.3 and has no need to send any legacy ciphers. Note this requires changing some test expectations. Removing all of TLS 1.1 and below's ciphers in TLS 1.3 has consequences for how a tripped minimum version reads. BUG=66 Change-Id: I695440ae78b95d9c7b5b921c3cb2eb43ea4cc50f Reviewed-on: https://boringssl-review.googlesource.com/8514 Reviewed-by: David Benjamin --- ssl/handshake_client.c | 12 ++++++------ ssl/test/runner/runner.go | 34 ++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index b625f37f4..8d8b1da2b 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -550,7 +550,9 @@ end: return ret; } -static int ssl3_write_client_cipher_list(SSL *ssl, CBB *out) { +static int ssl3_write_client_cipher_list(SSL *ssl, CBB *out, + uint16_t min_version, + uint16_t max_version) { /* Prepare disabled cipher masks. */ ssl_set_client_disabled(ssl); @@ -570,10 +572,8 @@ static int ssl3_write_client_cipher_list(SSL *ssl, CBB *out) { (cipher->algorithm_auth & ssl->cert->mask_a)) { continue; } - /* TODO(davidben): Also check |SSL_CIPHER_get_max_version| against the - * minimum enabled version. See https://crbug.com/boringssl/66. */ - if (SSL_CIPHER_get_min_version(cipher) > - ssl->method->version_from_wire(ssl->client_version)) { + if (SSL_CIPHER_get_min_version(cipher) > max_version || + SSL_CIPHER_get_max_version(cipher) < min_version) { continue; } any_enabled = 1; @@ -681,7 +681,7 @@ static int ssl3_send_client_hello(SSL *ssl) { size_t header_len = SSL_IS_DTLS(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH; - if (!ssl3_write_client_cipher_list(ssl, &body) || + if (!ssl3_write_client_cipher_list(ssl, &body, min_version, max_version) || !CBB_add_u8(&body, 1 /* one compression method */) || !CBB_add_u8(&body, 0 /* null compression */) || !ssl_add_clienthello_tlsext(ssl, &body, header_len + CBB_len(&body)) || diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 909a0e8ed..465faeb4f 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -3597,14 +3597,24 @@ func addMinimumVersionTests() { var expectedVersion uint16 var shouldFail bool - var expectedError string - var expectedLocalError string + var expectedClientError, expectedServerError string + var expectedClientLocalError, expectedServerLocalError string if runnerVers.version >= shimVers.version { expectedVersion = runnerVers.version } else { shouldFail = true - expectedError = ":UNSUPPORTED_PROTOCOL:" - expectedLocalError = "remote error: protocol version not supported" + expectedServerError = ":UNSUPPORTED_PROTOCOL:" + expectedServerLocalError = "remote error: protocol version not supported" + if shimVers.version >= VersionTLS13 && runnerVers.version <= VersionTLS11 { + // If the client's minimum version is TLS 1.3 and the runner's + // maximum is below TLS 1.2, the runner will fail to select a + // cipher before the shim rejects the selected version. + expectedClientError = ":SSLV3_ALERT_HANDSHAKE_FAILURE:" + expectedClientLocalError = "tls: no cipher suite supported by both client and server" + } else { + expectedClientError = expectedServerError + expectedClientLocalError = expectedServerLocalError + } } testCases = append(testCases, testCase{ @@ -3617,8 +3627,8 @@ func addMinimumVersionTests() { flags: flags, expectedVersion: expectedVersion, shouldFail: shouldFail, - expectedError: expectedError, - expectedLocalError: expectedLocalError, + expectedError: expectedClientError, + expectedLocalError: expectedClientLocalError, }) testCases = append(testCases, testCase{ protocol: protocol, @@ -3630,8 +3640,8 @@ func addMinimumVersionTests() { flags: []string{"-min-version", shimVersFlag}, expectedVersion: expectedVersion, shouldFail: shouldFail, - expectedError: expectedError, - expectedLocalError: expectedLocalError, + expectedError: expectedClientError, + expectedLocalError: expectedClientLocalError, }) testCases = append(testCases, testCase{ @@ -3644,8 +3654,8 @@ func addMinimumVersionTests() { flags: flags, expectedVersion: expectedVersion, shouldFail: shouldFail, - expectedError: expectedError, - expectedLocalError: expectedLocalError, + expectedError: expectedServerError, + expectedLocalError: expectedServerLocalError, }) testCases = append(testCases, testCase{ protocol: protocol, @@ -3657,8 +3667,8 @@ func addMinimumVersionTests() { flags: []string{"-min-version", shimVersFlag}, expectedVersion: expectedVersion, shouldFail: shouldFail, - expectedError: expectedError, - expectedLocalError: expectedLocalError, + expectedError: expectedServerError, + expectedLocalError: expectedServerLocalError, }) } } From d1e28ad53bec8e1ad6f642792ab663e7e509f67b Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 13:50:32 -0400 Subject: [PATCH 03/40] Remove key_exchange_info for plain RSA. This isn't filled in on the client and Chromium no longer uses it for plain RSA. It's redundant with existing APIs. This is part of removing the need for callers to call SSL_get_session where possible. SSL_get_session is ambiguous when it comes to renego. Some code wants the current connection state which should not include the pending handshake and some code wants the handshake scratch space which should. Renego doesn't exist in TLS 1.3, but TLS 1.3 makes NewSessionTicket a post-handshake message, so SSL_get_session is somewhat silly of an API there too. SSL_SESSION_get_key_exchange_info is a BoringSSL-only API, so we can freely change it and replace it with APIs keyed on SSL. In doing so, I think it is better to provide APIs like "SSL_get_dhe_group_size" and "SSL_get_curve_id" rather than make the caller do the multi-step SSL_get_current_cipher / SSL_CIPHER_is_ECDHE dance. To that end, RSA key_exchange_info is pointless as it can already be determined from the peer certificate. Change-Id: Ie90523083d8649701c17934b7be0383502a0caa3 Reviewed-on: https://boringssl-review.googlesource.com/8564 Reviewed-by: Adam Langley --- include/openssl/ssl.h | 1 - ssl/handshake_client.c | 1 - ssl/test/runner/runner.go | 13 ------------- 3 files changed, 15 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 59096e4c4..80988b079 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -3502,7 +3502,6 @@ struct ssl_session_st { * that controls the confidentiality of the connection. Its interpretation * depends on the primitive that was used; as specified by the cipher suite: * DHE: the size, in bits, of the multiplicative group. - * RSA: the size, in bits, of the modulus. * ECDHE: the TLS id for the curve. * * A zero indicates that the value is unknown. */ diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index 8d8b1da2b..3d70f7354 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -1694,7 +1694,6 @@ static int ssl3_send_client_key_exchange(SSL *ssl) { goto err; } - ssl->session->key_exchange_info = EVP_PKEY_bits(pkey); EVP_PKEY_free(pkey); pms[0] = ssl->client_version >> 8; diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 465faeb4f..32a695907 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -5246,19 +5246,6 @@ func addCECPQ1Tests() { } func addKeyExchangeInfoTests() { - testCases = append(testCases, testCase{ - name: "KeyExchangeInfo-RSA-Client", - config: Config{ - MaxVersion: VersionTLS12, - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, - }, - // key.pem is a 1024-bit RSA key. - flags: []string{"-expect-key-exchange-info", "1024"}, - }) - // TODO(davidben): key_exchange_info doesn't work for plain RSA on the - // server. Either fix this or change the API as it's not very useful in - // this case. - testCases = append(testCases, testCase{ name: "KeyExchangeInfo-DHE-Client", config: Config{ From 18a3518e5a94d817740ca89b4ddda63286565d03 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 13:46:24 -0400 Subject: [PATCH 04/40] Don't allocate a group/curve ID for CECPQ1. We ended up switching this from a curve to a cipher suite, so the group ID isn't used. This is in preparation for adding an API for the curve ID, at which point leaving the protocol constants undefined seems somewhat bad manners. Change-Id: Icb8bf4594879dbbc24177551868ecfe89bc2f8c3 Reviewed-on: https://boringssl-review.googlesource.com/8563 Reviewed-by: Adam Langley --- ssl/handshake_client.c | 4 +--- ssl/handshake_server.c | 4 ++-- ssl/internal.h | 4 +++- ssl/ssl_ecdh.c | 27 ++++++++++++++++----------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index 3d70f7354..b4123dd58 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -1274,9 +1274,7 @@ static int ssl3_get_server_key_exchange(SSL *ssl) { assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff); ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len; } else if (alg_k & SSL_kCECPQ1) { - if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, SSL_GROUP_CECPQ1)) { - goto err; - } + SSL_ECDH_CTX_init_for_cecpq1(&ssl->s3->tmp.ecdh_ctx); CBS key; if (!CBS_get_u16_length_prefixed(&server_key_exchange, &key)) { al = SSL_AD_DECODE_ERROR; diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index 133278e78..e0e1cd726 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c @@ -1223,8 +1223,8 @@ static int ssl3_send_server_key_exchange(SSL *ssl) { goto err; } } else if (alg_k & SSL_kCECPQ1) { - if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, SSL_GROUP_CECPQ1) || - !CBB_add_u16_length_prefixed(&cbb, &child) || + SSL_ECDH_CTX_init_for_cecpq1(&ssl->s3->tmp.ecdh_ctx); + if (!CBB_add_u16_length_prefixed(&cbb, &child) || !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) { goto err; } diff --git a/ssl/internal.h b/ssl/internal.h index c98172be2..41543eb8a 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -549,7 +549,6 @@ int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len); #define SSL_GROUP_SECP384R1 24 #define SSL_GROUP_SECP521R1 25 #define SSL_GROUP_X25519 29 -#define SSL_GROUP_CECPQ1 65165 /* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for * TLS. */ @@ -608,6 +607,9 @@ int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_id); * where the server specifies a group. It takes ownership of |params|. */ void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params); +/* SSL_ECDH_CTX_init_for_cecpq1 sets up |ctx| for use with CECPQ1. */ +void SSL_ECDH_CTX_init_for_cecpq1(SSL_ECDH_CTX *ctx); + /* SSL_ECDH_CTX_cleanup releases memory associated with |ctx|. It is legal to * call it in the zero state. */ void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx); diff --git a/ssl/ssl_ecdh.c b/ssl/ssl_ecdh.c index 1236cd398..4d7b63f4b 100644 --- a/ssl/ssl_ecdh.c +++ b/ssl/ssl_ecdh.c @@ -448,6 +448,16 @@ static const SSL_ECDH_METHOD kDHEMethod = { CBB_add_u16_length_prefixed, }; +static const SSL_ECDH_METHOD kCECPQ1Method = { + NID_undef, 0, "", + ssl_cecpq1_cleanup, + ssl_cecpq1_offer, + ssl_cecpq1_accept, + ssl_cecpq1_finish, + CBS_get_u16_length_prefixed, + CBB_add_u16_length_prefixed, +}; + static const SSL_ECDH_METHOD kMethods[] = { { NID_X9_62_prime256v1, @@ -493,17 +503,6 @@ static const SSL_ECDH_METHOD kMethods[] = { CBS_get_u8_length_prefixed, CBB_add_u8_length_prefixed, }, - { - NID_cecpq1, - SSL_GROUP_CECPQ1, - "CECPQ1", - ssl_cecpq1_cleanup, - ssl_cecpq1_offer, - ssl_cecpq1_accept, - ssl_cecpq1_finish, - CBS_get_u16_length_prefixed, - CBB_add_u16_length_prefixed, - }, }; static const SSL_ECDH_METHOD *method_from_group_id(uint16_t group_id) { @@ -562,6 +561,12 @@ void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params) { ctx->data = params; } +void SSL_ECDH_CTX_init_for_cecpq1(SSL_ECDH_CTX *ctx) { + SSL_ECDH_CTX_cleanup(ctx); + + ctx->method = &kCECPQ1Method; +} + int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out) { if (ctx->method == NULL) { return 0; From 727757694ead0c764930f8ad5e47afc1660a43a4 Mon Sep 17 00:00:00 2001 From: Steven Valdez Date: Tue, 14 Jun 2016 15:37:26 -0400 Subject: [PATCH 05/40] Adding new TLS 1.3 alert/extension IDs. Change-Id: Id8eb09b89010167d0f1e79d9d9e664d76020d959 Reviewed-on: https://boringssl-review.googlesource.com/8273 Reviewed-by: David Benjamin --- include/openssl/tls1.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 00eaacfb3..e28f352bd 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -157,6 +157,7 @@ extern "C" { #endif +#define TLS1_AD_END_OF_EARLY_DATA 1 #define TLS1_AD_DECRYPTION_FAILED 21 #define TLS1_AD_RECORD_OVERFLOW 22 #define TLS1_AD_UNKNOWN_CA 48 /* fatal */ @@ -169,6 +170,7 @@ extern "C" { #define TLS1_AD_INTERNAL_ERROR 80 /* fatal */ #define TLS1_AD_USER_CANCELLED 90 #define TLS1_AD_NO_RENEGOTIATION 100 +#define TLS1_AD_MISSING_EXTENSION 109 /* codes 110-114 are from RFC3546 */ #define TLS1_AD_UNSUPPORTED_EXTENSION 110 #define TLS1_AD_CERTIFICATE_UNOBTAINABLE 111 @@ -181,7 +183,7 @@ extern "C" { #define TLSEXT_TYPE_server_name 0 #define TLSEXT_TYPE_status_request 5 -/* ExtensionType values from draft-ietf-tls-tls13-latest */ +/* ExtensionType values from draft-ietf-tls-tls13-13 */ #define TLSEXT_TYPE_supported_groups 10 /* ExtensionType values from RFC4492 */ @@ -205,6 +207,13 @@ extern "C" { /* ExtensionType value from RFC4507 */ #define TLSEXT_TYPE_session_ticket 35 +/* ExtensionType values from draft-ietf-tls-tls13-latest */ +#define TLSEXT_TYPE_key_share 40 +#define TLSEXT_TYPE_pre_shared_key 41 +#define TLSEXT_TYPE_early_data 42 +#define TLSEXT_TYPE_ticket_age 43 +#define TLSEXT_TYPE_cookie 44 + /* ExtensionType value from RFC5746 */ #define TLSEXT_TYPE_renegotiate 0xff01 From 9e68f19e1b8ac53c301702d1d7079105a99df4e1 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 14:55:33 -0400 Subject: [PATCH 06/40] Add SSL_get_curve_id and SSL_get_dhe_group_size. This replaces the old key_exchange_info APIs and does not require the caller be aware of the mess around SSL_SESSION management. They currently have the same bugs around renegotiation as before, but later work to fix up SSL_SESSION tracking will fix their internals. For consistency with the existing functions, I've kept the public API at 'curve' rather than 'group' for now. I think it's probably better to have only one name with a single explanation in the section header rather than half and half. (I also wouldn't be surprised if the IETF ends up renaming 'group' again to 'key exchange' at some point. We'll see what happens.) Change-Id: I8e90a503bc4045d12f30835c86de64ef9f2d07c8 Reviewed-on: https://boringssl-review.googlesource.com/8565 Reviewed-by: Adam Langley --- include/openssl/ssl.h | 67 ++++++++++++++++++++++++++++----------- ssl/internal.h | 5 --- ssl/ssl_ecdh.c | 8 ++--- ssl/ssl_lib.c | 24 ++++++++++++++ ssl/t1_lib.c | 8 ++--- ssl/test/bssl_shim.cc | 20 +++++++++--- ssl/test/runner/runner.go | 8 ++--- ssl/test/test_config.cc | 4 +-- ssl/test/test_config.h | 3 +- 9 files changed, 103 insertions(+), 44 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 80988b079..641b94a42 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1506,13 +1506,6 @@ OPENSSL_EXPORT long SSL_SESSION_get_time(const SSL_SESSION *session); /* SSL_SESSION_get_timeout returns the lifetime of |session| in seconds. */ OPENSSL_EXPORT long SSL_SESSION_get_timeout(const SSL_SESSION *session); -/* SSL_SESSION_get_key_exchange_info returns a value that describes the - * strength of the asymmetric operation that provides confidentiality to - * |session|. Its interpretation depends on the operation used. See the - * documentation for this value in the |SSL_SESSION| structure. */ -OPENSSL_EXPORT uint32_t SSL_SESSION_get_key_exchange_info( - const SSL_SESSION *session); - /* SSL_SESSION_get0_peer return's the peer leaf certificate stored in * |session|. * @@ -1837,35 +1830,50 @@ OPENSSL_EXPORT int SSL_CTX_set_tlsext_ticket_key_cb( * are supported. ECDHE is always enabled, but the curve preferences may be * configured with these functions. * - * A client may use |SSL_SESSION_get_key_exchange_info| to determine the curve - * selected. */ + * Note that TLS 1.3 renames these from curves to groups. For consistency, we + * currently use the TLS 1.2 name in the API. */ /* SSL_CTX_set1_curves sets the preferred curves for |ctx| to be |curves|. Each * element of |curves| should be a curve nid. It returns one on success and - * zero on failure. */ + * zero on failure. + * + * Note that this API uses nid values from nid.h and not the |SSL_CURVE_*| + * values defined below. */ OPENSSL_EXPORT int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len); /* SSL_set1_curves sets the preferred curves for |ssl| to be |curves|. Each * element of |curves| should be a curve nid. It returns one on success and - * zero on failure. */ + * zero on failure. + * + * Note that this API uses nid values from nid.h and not the |SSL_CURVE_*| + * values defined below. */ OPENSSL_EXPORT int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len); -/* SSL_get_curve_name returns a human-readable name for the group specified by - * the given TLS group id, or NULL if the group is unknown. */ -OPENSSL_EXPORT const char *SSL_get_curve_name(uint16_t group_id); +/* SSL_CURVE_* define TLS curve IDs. */ +#define SSL_CURVE_SECP256R1 23 +#define SSL_CURVE_SECP384R1 24 +#define SSL_CURVE_SECP521R1 25 +#define SSL_CURVE_X25519 29 + +/* SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently + * completed handshake or 0 if not applicable. + * + * TODO(davidben): This API currently does not work correctly if there is a + * renegotiation in progress. Fix this. */ +OPENSSL_EXPORT uint16_t SSL_get_curve_id(const SSL *ssl); + +/* SSL_get_curve_name returns a human-readable name for the curve specified by + * the given TLS curve id, or NULL if the curve is unknown. */ +OPENSSL_EXPORT const char *SSL_get_curve_name(uint16_t curve_id); /* Multiplicative Diffie-Hellman. * * Cipher suites using a DHE key exchange perform Diffie-Hellman over a * multiplicative group selected by the server. These ciphers are disabled for a - * server unless a group is chosen with one of these functions. - * - * A client may use |SSL_SESSION_get_key_exchange_info| to determine the size of - * the selected group's prime, but note that servers may select degenerate - * groups. */ + * server unless a group is chosen with one of these functions. */ /* SSL_CTX_set_tmp_dh configures |ctx| to use the group from |dh| as the group * for DHE. Only the group is used, so |dh| needn't have a keypair. It returns @@ -1898,6 +1906,15 @@ OPENSSL_EXPORT void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*dh)(SSL *ssl, int is_export, int keylength)); +/* SSL_get_dhe_group_size returns the number of bits in the most recently + * completed handshake's selected group's prime, or zero if not + * applicable. Note, however, that validating this value does not ensure the + * server selected a secure group. + * + * TODO(davidben): This API currently does not work correctly if there is a + * renegotiation in progress. Fix this. */ +OPENSSL_EXPORT unsigned SSL_get_dhe_group_size(const SSL *ssl); + /* Certificate verification. * @@ -3458,6 +3475,18 @@ OPENSSL_EXPORT int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key); OPENSSL_EXPORT int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *out, const char *dir); +/* SSL_SESSION_get_key_exchange_info returns a value that describes the + * strength of the asymmetric operation that provides confidentiality to + * |session|. Its interpretation depends on the operation used. See the + * documentation for this value in the |SSL_SESSION| structure. + * + * Use |SSL_get_curve_id| or |SSL_get_dhe_group_size| instead. + * + * TODO(davidben): Remove this API once Chromium has switched to the new + * APIs. */ +OPENSSL_EXPORT uint32_t SSL_SESSION_get_key_exchange_info( + const SSL_SESSION *session); + /* Private structures. * diff --git a/ssl/internal.h b/ssl/internal.h index 41543eb8a..d0eca69ff 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -545,11 +545,6 @@ int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len); /* ECDH groups. */ -#define SSL_GROUP_SECP256R1 23 -#define SSL_GROUP_SECP384R1 24 -#define SSL_GROUP_SECP521R1 25 -#define SSL_GROUP_X25519 29 - /* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for * TLS. */ struct ssl_ecdh_method_st { diff --git a/ssl/ssl_ecdh.c b/ssl/ssl_ecdh.c index 4d7b63f4b..bc363e958 100644 --- a/ssl/ssl_ecdh.c +++ b/ssl/ssl_ecdh.c @@ -461,7 +461,7 @@ static const SSL_ECDH_METHOD kCECPQ1Method = { static const SSL_ECDH_METHOD kMethods[] = { { NID_X9_62_prime256v1, - SSL_GROUP_SECP256R1, + SSL_CURVE_SECP256R1, "P-256", ssl_ec_point_cleanup, ssl_ec_point_offer, @@ -472,7 +472,7 @@ static const SSL_ECDH_METHOD kMethods[] = { }, { NID_secp384r1, - SSL_GROUP_SECP384R1, + SSL_CURVE_SECP384R1, "P-384", ssl_ec_point_cleanup, ssl_ec_point_offer, @@ -483,7 +483,7 @@ static const SSL_ECDH_METHOD kMethods[] = { }, { NID_secp521r1, - SSL_GROUP_SECP521R1, + SSL_CURVE_SECP521R1, "P-521", ssl_ec_point_cleanup, ssl_ec_point_offer, @@ -494,7 +494,7 @@ static const SSL_ECDH_METHOD kMethods[] = { }, { NID_X25519, - SSL_GROUP_X25519, + SSL_CURVE_X25519, "X25519", ssl_x25519_cleanup, ssl_x25519_offer, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 4f90c4732..21e5207f3 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1345,6 +1345,18 @@ int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) { curves_len); } +uint16_t SSL_get_curve_id(const SSL *ssl) { + /* TODO(davidben): This checks the wrong session if there is a renegotiation in + * progress. */ + if (ssl->session == NULL || + ssl->session->cipher == NULL || + !SSL_CIPHER_is_ECDHE(ssl->session->cipher)) { + return 0; + } + + return (uint16_t)ssl->session->key_exchange_info; +} + int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) { DH_free(ctx->cert->dh_tmp); ctx->cert->dh_tmp = DHparams_dup(dh); @@ -2267,6 +2279,18 @@ void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*callback)(SSL *ssl, int is_export, ssl->cert->dh_tmp_cb = callback; } +unsigned SSL_get_dhe_group_size(const SSL *ssl) { + /* TODO(davidben): This checks the wrong session if there is a renegotiation in + * progress. */ + if (ssl->session == NULL || + ssl->session->cipher == NULL || + !SSL_CIPHER_is_DHE(ssl->session->cipher)) { + return 0; + } + + return ssl->session->key_exchange_info; +} + int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) { if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) { OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index e733b489d..9be9add1b 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -292,11 +292,11 @@ int SSL_early_callback_ctx_extension_get( } static const uint16_t kDefaultGroups[] = { - SSL_GROUP_X25519, - SSL_GROUP_SECP256R1, - SSL_GROUP_SECP384R1, + SSL_CURVE_X25519, + SSL_CURVE_SECP256R1, + SSL_CURVE_SECP384R1, #if defined(BORINGSSL_ANDROID_SYSTEM) - SSL_GROUP_SECP521R1, + SSL_CURVE_SECP521R1, #endif }; diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 99a5b93c7..851697731 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc @@ -1137,11 +1137,21 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) { return false; } - if (config->expect_key_exchange_info != 0) { - uint32_t info = SSL_SESSION_get_key_exchange_info(SSL_get_session(ssl)); - if (static_cast(config->expect_key_exchange_info) != info) { - fprintf(stderr, "key_exchange_info was %" PRIu32 ", wanted %" PRIu32 "\n", - info, static_cast(config->expect_key_exchange_info)); + if (config->expect_curve_id != 0) { + uint16_t curve_id = SSL_get_curve_id(ssl); + if (static_cast(config->expect_curve_id) != curve_id) { + fprintf(stderr, "curve_id was %04x, wanted %04x\n", curve_id, + static_cast(config->expect_curve_id)); + return false; + } + } + + if (config->expect_dhe_group_size != 0) { + unsigned dhe_group_size = SSL_get_dhe_group_size(ssl); + if (static_cast(config->expect_dhe_group_size) != + dhe_group_size) { + fprintf(stderr, "dhe_group_size was %u, wanted %d\n", dhe_group_size, + config->expect_dhe_group_size); return false; } } diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 32a695907..0ede7bb06 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -5258,7 +5258,7 @@ func addKeyExchangeInfoTests() { DHGroupPrime: bigFromHex("0215C589A86BE450D1255A86D7A08877A70E124C11F0C75E476BA6A2186B1C830D4A132555973F2D5881D5F737BB800B7F417C01EC5960AEBF79478F8E0BBB6A021269BD10590C64C57F50AD8169D5488B56EE38DC5E02DA1A16ED3B5F41FEB2AD184B78A31F3A5B2BEC8441928343DA35DE3D4F89F0D4CEDE0034045084A0D1E6182E5EF7FCA325DD33CE81BE7FA87D43613E8FA7A1457099AB53"), }, }, - flags: []string{"-expect-key-exchange-info", "1234"}, + flags: []string{"-expect-dhe-group-size", "1234"}, }) testCases = append(testCases, testCase{ testType: serverTest, @@ -5268,7 +5268,7 @@ func addKeyExchangeInfoTests() { CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, }, // bssl_shim as a server configures a 2048-bit DHE group. - flags: []string{"-expect-key-exchange-info", "2048"}, + flags: []string{"-expect-dhe-group-size", "2048"}, }) // TODO(davidben): Add TLS 1.3 versions of these tests once the @@ -5281,7 +5281,7 @@ func addKeyExchangeInfoTests() { CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, CurvePreferences: []CurveID{CurveX25519}, }, - flags: []string{"-expect-key-exchange-info", "29", "-enable-all-curves"}, + flags: []string{"-expect-curve-id", "29", "-enable-all-curves"}, }) testCases = append(testCases, testCase{ testType: serverTest, @@ -5291,7 +5291,7 @@ func addKeyExchangeInfoTests() { CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, CurvePreferences: []CurveID{CurveX25519}, }, - flags: []string{"-expect-key-exchange-info", "29", "-enable-all-curves"}, + flags: []string{"-expect-curve-id", "29", "-enable-all-curves"}, }) } diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index 536978b69..cf3541db8 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc @@ -147,8 +147,8 @@ const Flag kIntFlags[] = { { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations }, { "-expect-server-key-exchange-hash", &TestConfig::expect_server_key_exchange_hash }, - { "-expect-key-exchange-info", - &TestConfig::expect_key_exchange_info }, + { "-expect-curve-id", &TestConfig::expect_curve_id }, + { "-expect-dhe-group-size", &TestConfig::expect_dhe_group_size }, { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms }, }; diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index aff194ef4..b554f14ea 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h @@ -103,7 +103,8 @@ struct TestConfig { bool p384_only = false; bool enable_all_curves = false; bool use_sparse_dh_prime = false; - int expect_key_exchange_info = 0; + int expect_curve_id = 0; + int expect_dhe_group_size = 0; bool use_old_client_cert_callback = false; int initial_timeout_duration_ms = 0; }; From 0ba87732c659b92cbf879c09e18b8ae1f33422f0 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 19:06:08 -0400 Subject: [PATCH 07/40] Group 1.3 extension constants together and remove ticket_age. I'd meant to change the other -latest to -13 when I merged this, but we may as well group the two together anyway. Also remove ticket_age as that's likely to go away in PR#503. Change-Id: Ibb2f447e344d0b13c937291de69ace37ac9a5e8d Reviewed-on: https://boringssl-review.googlesource.com/8567 Reviewed-by: Steven Valdez Reviewed-by: David Benjamin --- include/openssl/tls1.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index e28f352bd..cfb314d77 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -183,9 +183,6 @@ extern "C" { #define TLSEXT_TYPE_server_name 0 #define TLSEXT_TYPE_status_request 5 -/* ExtensionType values from draft-ietf-tls-tls13-13 */ -#define TLSEXT_TYPE_supported_groups 10 - /* ExtensionType values from RFC4492 */ #define TLSEXT_TYPE_ec_point_formats 11 @@ -207,11 +204,11 @@ extern "C" { /* ExtensionType value from RFC4507 */ #define TLSEXT_TYPE_session_ticket 35 -/* ExtensionType values from draft-ietf-tls-tls13-latest */ +/* ExtensionType values from draft-ietf-tls-tls13-13 */ +#define TLSEXT_TYPE_supported_groups 10 #define TLSEXT_TYPE_key_share 40 #define TLSEXT_TYPE_pre_shared_key 41 #define TLSEXT_TYPE_early_data 42 -#define TLSEXT_TYPE_ticket_age 43 #define TLSEXT_TYPE_cookie 44 /* ExtensionType value from RFC5746 */ From 2b8415e8ff324cd4a322670a5a1b6cb8906ebcfd Mon Sep 17 00:00:00 2001 From: Steven Valdez Date: Thu, 30 Jun 2016 13:27:23 -0400 Subject: [PATCH 08/40] Move the Digest/Sign split for SignatureAlgorithms to a lower level. In order to delay the digest of the handshake transcript and unify around message-based signing callbacks, a copy of the transcript is kept around until we are sure there is no certificate authentication. This removes support for SSL_PRIVATE_KEY_METHOD as a client in SSL 3.0. Change-Id: If8999a19ca021b4ff439319ab91e2cd2103caa64 Reviewed-on: https://boringssl-review.googlesource.com/8561 Reviewed-by: David Benjamin --- crypto/err/ssl.errordata | 1 + include/openssl/ssl.h | 1 + ssl/handshake_client.c | 86 ++++++++++++++++++----------- ssl/handshake_server.c | 115 +++++++++++++++++++-------------------- ssl/internal.h | 17 +++--- ssl/s3_both.c | 49 ----------------- ssl/s3_enc.c | 78 ++++++++++++++++---------- ssl/ssl_rsa.c | 38 ++++++++++++- ssl/t1_enc.c | 24 -------- 9 files changed, 205 insertions(+), 204 deletions(-) diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata index 64249ad2e..700f9df12 100644 --- a/crypto/err/ssl.errordata +++ b/crypto/err/ssl.errordata @@ -171,6 +171,7 @@ SSL,237,UNSUPPORTED_CIPHER SSL,238,UNSUPPORTED_COMPRESSION_ALGORITHM SSL,239,UNSUPPORTED_ELLIPTIC_CURVE SSL,240,UNSUPPORTED_PROTOCOL +SSL,252,UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY SSL,241,WRONG_CERTIFICATE_TYPE SSL,242,WRONG_CIPHER_RETURNED SSL,243,WRONG_CURVE diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 641b94a42..627c288fd 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -4655,6 +4655,7 @@ OPENSSL_EXPORT int SSL_set_ssl_method(SSL *s, const SSL_METHOD *method); #define SSL_R_X509_VERIFICATION_SETUP_PROBLEMS 249 #define SSL_R_SHUTDOWN_WHILE_IN_INIT 250 #define SSL_R_INVALID_OUTER_RECORD_TYPE 251 +#define SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY 252 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020 diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index b4123dd58..e3cd10229 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -880,11 +880,10 @@ static int ssl3_get_server_hello(SSL *ssl) { goto f_err; } - /* If doing a full handshake with TLS 1.2, the server may request a client - * certificate which requires hashing the handshake transcript under a - * different hash. Otherwise, the handshake buffer may be released. */ - if (ssl->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION || - !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) { + /* If doing a full handshake, the server may request a client certificate + * which requires hashing the handshake transcript. Otherwise, the handshake + * buffer may be released. */ + if (ssl->hit || !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) { ssl3_free_handshake_buffer(ssl); } @@ -1120,7 +1119,6 @@ static int ssl3_verify_server_cert(SSL *ssl) { } static int ssl3_get_server_key_exchange(SSL *ssl) { - EVP_MD_CTX md_ctx; int al, ok; EVP_PKEY *pkey = NULL; DH *dh = NULL; @@ -1158,7 +1156,6 @@ static int ssl3_get_server_key_exchange(SSL *ssl) { uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey; uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth; - EVP_MD_CTX_init(&md_ctx); if (alg_a & SSL_aPSK) { CBS psk_identity_hint; @@ -1336,21 +1333,25 @@ static int ssl3_get_server_key_exchange(SSL *ssl) { goto f_err; } - const EVP_MD *md = tls12_get_hash(signature_algorithm); - if (md == NULL) { - al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + CBB transcript; + uint8_t *transcript_data; + size_t transcript_len; + if (!CBB_init(&transcript, 2*SSL3_RANDOM_SIZE + CBS_len(¶meter)) || + !CBB_add_bytes(&transcript, ssl->s3->client_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, ssl->s3->server_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, CBS_data(¶meter), CBS_len(¶meter)) || + !CBB_finish(&transcript, &transcript_data, &transcript_len)) { + CBB_cleanup(&transcript); + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); goto f_err; } - int sig_ok = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) && - EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->client_random, - SSL3_RANDOM_SIZE) && - EVP_DigestVerifyUpdate(&md_ctx, ssl->s3->server_random, - SSL3_RANDOM_SIZE) && - EVP_DigestVerifyUpdate(&md_ctx, CBS_data(¶meter), - CBS_len(¶meter)) && - EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature), - CBS_len(&signature)); + + int sig_ok = ssl_public_key_verify( + ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm, + pkey, transcript_data, transcript_len); + OPENSSL_free(transcript_data); + #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) sig_ok = 1; ERR_clear_error(); @@ -1372,7 +1373,6 @@ static int ssl3_get_server_key_exchange(SSL *ssl) { } } EVP_PKEY_free(pkey); - EVP_MD_CTX_cleanup(&md_ctx); return 1; f_err: @@ -1382,7 +1382,6 @@ err: DH_free(dh); EC_POINT_free(srvr_ecpoint); EC_KEY_free(ecdh); - EVP_MD_CTX_cleanup(&md_ctx); return -1; } @@ -1840,23 +1839,44 @@ static int ssl3_send_cert_verify(SSL *ssl) { goto err; } - size_t sig_len; + size_t sig_len = max_sig_len; enum ssl_private_key_result_t sign_result; if (ssl->state == SSL3_ST_CW_CERT_VRFY_A) { - /* Compute the digest. In TLS 1.1 and below, the digest type is also - * selected here. */ - uint8_t digest[EVP_MAX_MD_SIZE]; - size_t digest_len; - if (!ssl3_cert_verify_hash(ssl, digest, &digest_len, signature_algorithm)) { - goto err; + /* The SSL3 construction for CertificateVerify does not decompose into a + * single final digest and signature, and must be special-cased. */ + if (ssl3_protocol_version(ssl) == SSL3_VERSION) { + if (ssl->cert->key_method != NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY); + goto err; + } + + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!ssl3_cert_verify_hash(ssl, digest, &digest_len, + signature_algorithm)) { + goto err; + } + + sign_result = ssl_private_key_success; + + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL); + if (pctx == NULL || + !EVP_PKEY_sign_init(pctx) || + !EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len)) { + EVP_PKEY_CTX_free(pctx); + sign_result = ssl_private_key_failure; + goto err; + } + EVP_PKEY_CTX_free(pctx); + } else { + sign_result = ssl_private_key_sign( + ssl, ptr, &sig_len, max_sig_len, signature_algorithm, + (const uint8_t *)ssl->s3->handshake_buffer->data, + ssl->s3->handshake_buffer->length); } /* The handshake buffer is no longer necessary. */ ssl3_free_handshake_buffer(ssl); - - /* Sign the digest. */ - sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len, - signature_algorithm, digest, digest_len); } else { assert(ssl->state == SSL3_ST_CW_CERT_VRFY_B); sign_result = diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index e0e1cd726..d302f1154 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c @@ -1036,10 +1036,8 @@ static int ssl3_get_client_hello(SSL *ssl) { goto f_err; } - /* In TLS 1.2, client authentication requires hashing the handshake transcript - * under a different hash. Otherwise, release the handshake buffer. */ - if (!ssl->s3->tmp.cert_request || - ssl3_protocol_version(ssl) < TLS1_2_VERSION) { + /* Release the handshake buffer if client authentication isn't required. */ + if (!ssl->s3->tmp.cert_request) { ssl3_free_handshake_buffer(ssl); } @@ -1279,33 +1277,26 @@ static int ssl3_send_server_key_exchange(SSL *ssl) { size_t sig_len; enum ssl_private_key_result_t sign_result; if (ssl->state == SSL3_ST_SW_KEY_EXCH_A) { - /* Compute the digest and sign it. */ - uint8_t digest[EVP_MAX_MD_SIZE]; - unsigned digest_len = 0; - EVP_MD_CTX md_ctx; - EVP_MD_CTX_init(&md_ctx); - - const EVP_MD *md = tls12_get_hash(signature_algorithm); - if (md == NULL) { - OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); - ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + CBB transcript; + uint8_t *transcript_data; + size_t transcript_len; + if (!CBB_init(&transcript, + 2*SSL3_RANDOM_SIZE + ssl->s3->tmp.server_params_len) || + !CBB_add_bytes(&transcript, ssl->s3->client_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, ssl->s3->server_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, ssl->s3->tmp.server_params, + ssl->s3->tmp.server_params_len) || + !CBB_finish(&transcript, &transcript_data, &transcript_len)) { + CBB_cleanup(&transcript); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); goto err; } - int digest_ret = - EVP_DigestInit_ex(&md_ctx, md, NULL) && - EVP_DigestUpdate(&md_ctx, ssl->s3->client_random, SSL3_RANDOM_SIZE) && - EVP_DigestUpdate(&md_ctx, ssl->s3->server_random, SSL3_RANDOM_SIZE) && - EVP_DigestUpdate(&md_ctx, ssl->s3->tmp.server_params, - ssl->s3->tmp.server_params_len) && - EVP_DigestFinal_ex(&md_ctx, digest, &digest_len); - EVP_MD_CTX_cleanup(&md_ctx); - if (!digest_ret) { - goto err; - } sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len, - signature_algorithm, digest, - digest_len); + signature_algorithm, transcript_data, + transcript_len); + OPENSSL_free(transcript_data); } else { assert(ssl->state == SSL3_ST_SW_KEY_EXCH_B); sign_result = @@ -1876,9 +1867,6 @@ static int ssl3_get_cert_verify(SSL *ssl) { CBS certificate_verify, signature; X509 *peer = ssl->session->peer; EVP_PKEY *pkey = NULL; - uint8_t digest[EVP_MAX_MD_SIZE]; - size_t digest_length; - EVP_PKEY_CTX *pctx = NULL; /* Only RSA and ECDSA client certificates are supported, so a * CertificateVerify is required if and only if there's a client certificate. @@ -1927,19 +1915,6 @@ static int ssl3_get_cert_verify(SSL *ssl) { signature_algorithm = SSL_SIGN_ECDSA_SHA1; } - /* Compute the digest. */ - if (!ssl3_cert_verify_hash(ssl, digest, &digest_length, - signature_algorithm)) { - goto err; - } - - /* The handshake buffer is no longer necessary, and we may hash the current - * message.*/ - ssl3_free_handshake_buffer(ssl); - if (!ssl3_hash_current_message(ssl)) { - goto err; - } - /* Parse and verify the signature. */ if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) || CBS_len(&certificate_verify) != 0) { @@ -1948,22 +1923,40 @@ static int ssl3_get_cert_verify(SSL *ssl) { goto f_err; } - pctx = EVP_PKEY_CTX_new(pkey, NULL); - if (pctx == NULL) { - goto err; + int sig_ok; + /* The SSL3 construction for CertificateVerify does not decompose into a + * single final digest and signature, and must be special-cased. */ + if (ssl3_protocol_version(ssl) == SSL3_VERSION) { + if (ssl->cert->key_method != NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY); + goto err; + } + + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!ssl3_cert_verify_hash(ssl, digest, &digest_len, + signature_algorithm)) { + goto err; + } + + const EVP_MD *md = tls12_get_hash(signature_algorithm); + assert(md != NULL); + + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(pkey, NULL); + sig_ok = pctx != NULL && + EVP_PKEY_verify_init(pctx) && + EVP_PKEY_CTX_set_signature_md(pctx, md) && + EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature), + digest, digest_len); + + EVP_PKEY_CTX_free(pctx); + } else { + sig_ok = ssl_public_key_verify( + ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm, + pkey, (const uint8_t *)ssl->s3->handshake_buffer->data, + ssl->s3->handshake_buffer->length); } - const EVP_MD *md = tls12_get_hash(signature_algorithm); - if (md == NULL) { - al = SSL_AD_ILLEGAL_PARAMETER; - OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); - goto f_err; - } - - int sig_ok = EVP_PKEY_verify_init(pctx) && - EVP_PKEY_CTX_set_signature_md(pctx, md) && - EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature), - digest, digest_length); #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) sig_ok = 1; ERR_clear_error(); @@ -1974,6 +1967,13 @@ static int ssl3_get_cert_verify(SSL *ssl) { goto f_err; } + /* The handshake buffer is no longer necessary, and we may hash the current + * message.*/ + ssl3_free_handshake_buffer(ssl); + if (!ssl3_hash_current_message(ssl)) { + goto err; + } + ret = 1; if (0) { @@ -1982,7 +1982,6 @@ static int ssl3_get_cert_verify(SSL *ssl) { } err: - EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(pkey); return ret; diff --git a/ssl/internal.h b/ssl/internal.h index d0eca69ff..ab79dcc67 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -490,6 +490,12 @@ enum ssl_private_key_result_t ssl_private_key_decrypt( enum ssl_private_key_result_t ssl_private_key_decrypt_complete( SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out); +/* ssl_public_key_verify verifies that the |signature| is valid for the public + * key |pkey| and input |in|, using the |signature_algorithm| specified. */ +int ssl_public_key_verify( + SSL *ssl, const uint8_t *signature, size_t signature_len, + uint16_t signature_algorithm, EVP_PKEY *pkey, + const uint8_t *in, size_t in_len); /* Custom extensions */ @@ -865,7 +871,6 @@ struct ssl3_enc_method { size_t label_len, const uint8_t *seed1, size_t seed1_len, const uint8_t *seed2, size_t seed2_len); int (*final_finish_mac)(SSL *ssl, int from_server, uint8_t *out); - int (*cert_verify_mac)(SSL *, int, uint8_t *); }; /* lengths of messages */ @@ -1008,12 +1013,10 @@ long ssl3_get_message(SSL *ssl, int msg_type, * handshake hash. It returns one on success and zero on allocation failure. */ int ssl3_hash_current_message(SSL *ssl); -/* ssl3_cert_verify_hash writes the CertificateVerify hash into the bytes - * pointed to by |out| and writes the number of bytes to |*out_len|. |out| must - * have room for EVP_MAX_MD_SIZE bytes. For TLS 1.2 and up, - * |signature_algorithm| is used to determine the hash function, otherwise the - * hash function depends on the private key type. It returns one on success and - * zero on failure. */ +/* ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify hash into the + * bytes pointed to by |out| and writes the number of bytes to |*out_len|. |out| + * must have room for EVP_MAX_MD_SIZE bytes. It returns one on success and zero + * on failure. */ int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len, uint16_t signature_algorithm); diff --git a/ssl/s3_both.c b/ssl/s3_both.c index 8dee4d8cb..f28e50f38 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c @@ -463,55 +463,6 @@ int ssl3_hash_current_message(SSL *ssl) { ssl->init_num + header_len); } -/* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that - * is sufficient pre-TLS1.2 as well. */ -OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH, - combined_tls_hash_fits_in_max); - -int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len, - uint16_t signature_algorithm) { - /* For TLS v1.2 send signature algorithm and signature using - * agreed digest and cached handshake records. Otherwise, use - * SHA1 or MD5 + SHA1 depending on key type. */ - if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { - EVP_MD_CTX mctx; - unsigned len; - - const EVP_MD *md = tls12_get_hash(signature_algorithm); - if (md == NULL) { - return 0; - } - - EVP_MD_CTX_init(&mctx); - if (!EVP_DigestInit_ex(&mctx, md, NULL) || - !EVP_DigestUpdate(&mctx, ssl->s3->handshake_buffer->data, - ssl->s3->handshake_buffer->length) || - !EVP_DigestFinal(&mctx, out, &len)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); - EVP_MD_CTX_cleanup(&mctx); - return 0; - } - *out_len = len; - } else if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) { - if (ssl->s3->enc_method->cert_verify_mac(ssl, NID_md5, out) == 0 || - ssl->s3->enc_method->cert_verify_mac(ssl, NID_sha1, - out + MD5_DIGEST_LENGTH) == 0) { - return 0; - } - *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH; - } else if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) { - if (ssl->s3->enc_method->cert_verify_mac(ssl, NID_sha1, out) == 0) { - return 0; - } - *out_len = SHA_DIGEST_LENGTH; - } else { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; - } - - return 1; -} - int ssl_verify_alarm_type(long type) { int al; diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index 04aa08c9f..d70f3731e 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -146,10 +146,6 @@ #include "internal.h" - -static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, - size_t sender_len, uint8_t *p); - static int ssl3_prf(const SSL *ssl, uint8_t *out, size_t out_len, const uint8_t *secret, size_t secret_len, const char *label, size_t label_len, const uint8_t *seed1, size_t seed1_len, @@ -294,31 +290,6 @@ int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len) { return 1; } -static int ssl3_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *p) { - return ssl3_handshake_mac(ssl, md_nid, NULL, 0, p); -} - -static int ssl3_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) { - const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST - : SSL3_MD_CLIENT_FINISHED_CONST; - const size_t sender_len = 4; - int ret, sha1len; - ret = ssl3_handshake_mac(ssl, NID_md5, sender, sender_len, out); - if (ret == 0) { - return 0; - } - - out += ret; - - sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, sender_len, out); - if (sha1len == 0) { - return 0; - } - - ret += sha1len; - return ret; -} - static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, size_t sender_len, uint8_t *p) { unsigned int ret; @@ -385,10 +356,57 @@ static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender, return ret; } +static int ssl3_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) { + const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST + : SSL3_MD_CLIENT_FINISHED_CONST; + const size_t sender_len = 4; + int ret, sha1len; + ret = ssl3_handshake_mac(ssl, NID_md5, sender, sender_len, out); + if (ret == 0) { + return 0; + } + out += ret; + + sha1len = ssl3_handshake_mac(ssl, NID_sha1, sender, sender_len, out); + if (sha1len == 0) { + return 0; + } + + ret += sha1len; + return ret; +} + +/* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that + * is sufficient pre-TLS1.2 as well. */ +OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH, + combined_tls_hash_fits_in_max); + +int ssl3_cert_verify_hash(SSL *ssl, uint8_t *out, size_t *out_len, + uint16_t signature_algorithm) { + assert(ssl3_protocol_version(ssl) == SSL3_VERSION); + + if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) { + if (ssl3_handshake_mac(ssl, NID_md5, NULL, 0, out) == 0 || + ssl3_handshake_mac(ssl, NID_sha1, NULL, 0, + out + MD5_DIGEST_LENGTH) == 0) { + return 0; + } + *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH; + } else if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) { + if (ssl3_handshake_mac(ssl, NID_sha1, NULL, 0, out) == 0) { + return 0; + } + *out_len = SHA_DIGEST_LENGTH; + } else { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} const SSL3_ENC_METHOD SSLv3_enc_data = { ssl3_prf, ssl3_final_finish_mac, - ssl3_cert_verify_mac, }; diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c index 66e73b4d2..a65f6f49a 100644 --- a/ssl/ssl_rsa.c +++ b/ssl/ssl_rsa.c @@ -372,9 +372,24 @@ enum ssl_private_key_result_t ssl_private_key_sign( return ssl_private_key_failure; } + EVP_MD_CTX mctx; + uint8_t hash[EVP_MAX_MD_SIZE]; + unsigned hash_len; + + EVP_MD_CTX_init(&mctx); + if (!EVP_DigestInit_ex(&mctx, md, NULL) || + !EVP_DigestUpdate(&mctx, in, in_len) || + !EVP_DigestFinal(&mctx, hash, &hash_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); + EVP_MD_CTX_cleanup(&mctx); + return 0; + } + EVP_MD_CTX_cleanup(&mctx); + + if (ssl->cert->key_method != NULL) { - return ssl->cert->key_method->sign(ssl, out, out_len, max_out, md, in, - in_len); + return ssl->cert->key_method->sign(ssl, out, out_len, max_out, md, hash, + hash_len); } enum ssl_private_key_result_t ret = ssl_private_key_failure; @@ -386,7 +401,7 @@ enum ssl_private_key_result_t ssl_private_key_sign( size_t len = max_out; if (!EVP_PKEY_sign_init(ctx) || !EVP_PKEY_CTX_set_signature_md(ctx, md) || - !EVP_PKEY_sign(ctx, out, &len, in, in_len)) { + !EVP_PKEY_sign(ctx, out, &len, hash, hash_len)) { goto end; } *out_len = len; @@ -403,6 +418,23 @@ enum ssl_private_key_result_t ssl_private_key_sign_complete( return ssl->cert->key_method->sign_complete(ssl, out, out_len, max_out); } +int ssl_public_key_verify(SSL *ssl, const uint8_t *signature, + size_t signature_len, uint16_t signature_algorithm, + EVP_PKEY *pkey, const uint8_t *in, size_t in_len) { + const EVP_MD *md = tls12_get_hash(signature_algorithm); + if (md == NULL) { + return 0; + } + + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + int ret = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) && + EVP_DigestVerifyUpdate(&md_ctx, in, in_len) && + EVP_DigestVerifyFinal(&md_ctx, signature, signature_len); + EVP_MD_CTX_cleanup(&md_ctx); + return ret; +} + enum ssl_private_key_result_t ssl_private_key_decrypt( SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, const uint8_t *in, size_t in_len) { diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index b59920772..453adf340 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -389,29 +389,6 @@ int tls1_setup_key_block(SSL *ssl) { return 1; } -static int tls1_cert_verify_mac(SSL *ssl, int md_nid, uint8_t *out) { - const EVP_MD_CTX *ctx_template; - if (md_nid == NID_md5) { - ctx_template = &ssl->s3->handshake_md5; - } else if (md_nid == EVP_MD_CTX_type(&ssl->s3->handshake_hash)) { - ctx_template = &ssl->s3->handshake_hash; - } else { - OPENSSL_PUT_ERROR(SSL, SSL_R_NO_REQUIRED_DIGEST); - return 0; - } - - EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); - if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) { - EVP_MD_CTX_cleanup(&ctx); - return 0; - } - unsigned ret; - EVP_DigestFinal_ex(&ctx, out, &ret); - EVP_MD_CTX_cleanup(&ctx); - return ret; -} - static int append_digest(const EVP_MD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out) { int ret = 0; @@ -558,5 +535,4 @@ int SSL_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len, const SSL3_ENC_METHOD TLSv1_enc_data = { tls1_prf, tls1_final_finish_mac, - tls1_cert_verify_mac, }; From b72f66f59cc5c8db51f6b60f2f5b97b56dc372f3 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 1 Jul 2016 11:34:10 -1000 Subject: [PATCH 09/40] Test |BN_mod_exp_mont| with zero and even modulus too. |BN_mod_exp_mont| should be tested the same way as the other variants, especially since it is exported. Change-Id: I8c05725289c0ebcce7aba7e666915c4c1a841c2b Reviewed-on: https://boringssl-review.googlesource.com/8590 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 446fd272b..ccce995a6 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -1542,6 +1542,13 @@ static bool TestBadModulus(BN_CTX *ctx) { } ERR_clear_error(); + if (BN_mod_exp_mont(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx, + NULL)) { + fprintf(stderr, "BN_mod_exp_mont with zero modulus succeeded!\n"); + return 0; + } + ERR_clear_error(); + if (BN_mod_exp_mont_consttime(a.get(), BN_value_one(), BN_value_one(), zero.get(), ctx, nullptr)) { fprintf(stderr, "BN_mod_exp_mont_consttime with zero modulus succeeded!\n"); @@ -1567,6 +1574,13 @@ static bool TestBadModulus(BN_CTX *ctx) { } ERR_clear_error(); + if (BN_mod_exp_mont(a.get(), BN_value_one(), BN_value_one(), b.get(), ctx, + NULL)) { + fprintf(stderr, "BN_mod_exp_mont with even modulus succeeded!\n"); + return 0; + } + ERR_clear_error(); + if (BN_mod_exp_mont_consttime(a.get(), BN_value_one(), BN_value_one(), b.get(), ctx, nullptr)) { fprintf(stderr, "BN_mod_exp_mont_consttime with even modulus succeeded!\n"); From 84cd159badd399847703da2c0c68cee8e405b3e1 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Thu, 30 Jun 2016 18:29:44 -0700 Subject: [PATCH 10/40] Add SSL_CTX_up_ref. Upstream added this in a18a31e49d266. The various *_up_ref functions return a variety of types, but this one returns int because upstream appears to be trying to unify around that. (See upstream's c5ebfcab713.) Change-Id: I7e1cfe78c3a32f5a85b1b3c14428bd91548aba6d Reviewed-on: https://boringssl-review.googlesource.com/8581 Reviewed-by: Adam Langley --- include/openssl/ssl.h | 3 +++ ssl/ssl_lib.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 627c288fd..3bbdb1750 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -194,6 +194,9 @@ OPENSSL_EXPORT const SSL_METHOD *DTLS_method(void); * on error. */ OPENSSL_EXPORT SSL_CTX *SSL_CTX_new(const SSL_METHOD *method); +/* SSL_CTX_up_ref increments the reference count of |ctx|. It returns one. */ +OPENSSL_EXPORT int SSL_CTX_up_ref(SSL_CTX *ctx); + /* SSL_CTX_free releases memory associated with |ctx|. */ OPENSSL_EXPORT void SSL_CTX_free(SSL_CTX *ctx); diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 21e5207f3..534480a9e 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -314,6 +314,11 @@ err2: return NULL; } +int SSL_CTX_up_ref(SSL_CTX *ctx) { + CRYPTO_refcount_inc(&ctx->references); + return 1; +} + void SSL_CTX_free(SSL_CTX *ctx) { if (ctx == NULL || !CRYPTO_refcount_dec_and_test_zero(&ctx->references)) { From fe47ba2fc5512436696f745b5756d08c7d8ceb0b Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 1 Jul 2016 11:46:21 -1000 Subject: [PATCH 11/40] Test |BN_add_word| and |BN_sub_word|. Change-Id: If2be0632aef7f2be1c43650e993a89518b354f60 Reviewed-on: https://boringssl-review.googlesource.com/8591 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index ccce995a6..0c9d74e88 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -312,6 +312,19 @@ static bool TestSum(FileTest *t, BN_CTX *ctx) { return false; } + // Test with |BN_add_word| and |BN_sub_word| if |b| is small enough. + BN_ULONG b_word = BN_get_word(b.get()); + if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) { + if (!BN_copy(ret.get(), a.get()) || + !BN_add_word(ret.get(), b_word) || + !ExpectBIGNUMsEqual(t, "A + B (word)", sum.get(), ret.get()) || + !BN_copy(ret.get(), sum.get()) || + !BN_sub_word(ret.get(), b_word) || + !ExpectBIGNUMsEqual(t, "Sum - B (word)", a.get(), ret.get())) { + return false; + } + } + return true; } From e4bf8b3e05ae87ef14675801997ccf3df7b86fce Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 1 Jul 2016 13:36:06 -1000 Subject: [PATCH 12/40] Test aliasing in |BN_add| and |BN_sub|. Also update the documentation for |BN_sub|. Change-Id: I544dbfc56f22844f6ca08e9e472ec13e76baf8c4 Reviewed-on: https://boringssl-review.googlesource.com/8592 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 24 ++++++++++++++++++++++++ include/openssl/bn.h | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 0c9d74e88..6248aaa95 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -312,6 +312,30 @@ static bool TestSum(FileTest *t, BN_CTX *ctx) { return false; } + // Test that the functions work when |r| and |a| point to the same |BIGNUM|, + // or when |r| and |b| point to the same |BIGNUM|. TODO: Test the case where + // all of |r|, |a|, and |b| point to the same |BIGNUM|. + if (!BN_copy(ret.get(), a.get()) || + !BN_add(ret.get(), ret.get(), b.get()) || + !ExpectBIGNUMsEqual(t, "A + B (r is a)", sum.get(), ret.get()) || + !BN_copy(ret.get(), b.get()) || + !BN_add(ret.get(), a.get(), ret.get()) || + !ExpectBIGNUMsEqual(t, "A + B (r is b)", sum.get(), ret.get()) || + !BN_copy(ret.get(), sum.get()) || + !BN_sub(ret.get(), ret.get(), a.get()) || + !ExpectBIGNUMsEqual(t, "Sum - A (r is a)", b.get(), ret.get()) || + !BN_copy(ret.get(), a.get()) || + !BN_sub(ret.get(), sum.get(), ret.get()) || + !ExpectBIGNUMsEqual(t, "Sum - A (r is b)", b.get(), ret.get()) || + !BN_copy(ret.get(), sum.get()) || + !BN_sub(ret.get(), ret.get(), b.get()) || + !ExpectBIGNUMsEqual(t, "Sum - B (r is a)", a.get(), ret.get()) || + !BN_copy(ret.get(), b.get()) || + !BN_sub(ret.get(), sum.get(), ret.get()) || + !ExpectBIGNUMsEqual(t, "Sum - B (r is b)", a.get(), ret.get())) { + return false; + } + // Test with |BN_add_word| and |BN_sub_word| if |b| is small enough. BN_ULONG b_word = BN_get_word(b.get()); if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) { diff --git a/include/openssl/bn.h b/include/openssl/bn.h index 9ee223761..bc715b02f 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -383,8 +383,8 @@ OPENSSL_EXPORT int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); /* BN_add_word adds |w| to |a|. It returns one on success and zero otherwise. */ OPENSSL_EXPORT int BN_add_word(BIGNUM *a, BN_ULONG w); -/* BN_sub sets |r| = |a| - |b|, where |r| must be a distinct pointer from |a| - * and |b|. It returns one on success and zero on allocation failure. */ +/* BN_sub sets |r| = |a| - |b|, where |r| may be the same pointer as either |a| + * or |b|. It returns one on success and zero on allocation failure. */ OPENSSL_EXPORT int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); /* BN_usub sets |r| = |a| - |b|, where |a| and |b| are non-negative integers, From 3d4030b5f7582f9cd686bfa81c7e09334ee18887 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Fri, 1 Jul 2016 14:12:40 -1000 Subject: [PATCH 13/40] Test |BN_uadd| and |BN_usub|. Also, update the documentation about aliasing for |BN_usub|. It might be better to find a way to factor out the shared logic between the tests of these functions and the tests of |BN_add| and |BN_usub|, but doing so would end up up creating a lot of parameters due to the many distinct strings used in the messages. Change-Id: Ic9d714858212fc92aa6bbcc3959576fe6bbf58c3 Reviewed-on: https://boringssl-review.googlesource.com/8593 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 40 ++++++++++++++++++++++++++++++++++++++++ include/openssl/bn.h | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 6248aaa95..fe6e0b747 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -336,6 +336,46 @@ static bool TestSum(FileTest *t, BN_CTX *ctx) { return false; } + // Test |BN_uadd| and |BN_usub| with the prerequisites they are documented as + // having. Note that these functions are frequently used when the + // prerequisites don't hold. In those cases, they are supposed to work as if + // the prerequisite hold, but we don't test that yet. TODO: test that. + if (!BN_is_negative(a.get()) && + !BN_is_negative(b.get()) && BN_cmp(a.get(), b.get()) >= 0) { + if (!BN_uadd(ret.get(), a.get(), b.get()) || + !ExpectBIGNUMsEqual(t, "A +u B", sum.get(), ret.get()) || + !BN_usub(ret.get(), sum.get(), a.get()) || + !ExpectBIGNUMsEqual(t, "Sum -u A", b.get(), ret.get()) || + !BN_usub(ret.get(), sum.get(), b.get()) || + !ExpectBIGNUMsEqual(t, "Sum -u B", a.get(), ret.get())) { + return false; + } + + // Test that the functions work when |r| and |a| point to the same |BIGNUM|, + // or when |r| and |b| point to the same |BIGNUM|. TODO: Test the case where + // all of |r|, |a|, and |b| point to the same |BIGNUM|. + if (!BN_copy(ret.get(), a.get()) || + !BN_uadd(ret.get(), ret.get(), b.get()) || + !ExpectBIGNUMsEqual(t, "A +u B (r is a)", sum.get(), ret.get()) || + !BN_copy(ret.get(), b.get()) || + !BN_uadd(ret.get(), a.get(), ret.get()) || + !ExpectBIGNUMsEqual(t, "A +u B (r is b)", sum.get(), ret.get()) || + !BN_copy(ret.get(), sum.get()) || + !BN_usub(ret.get(), ret.get(), a.get()) || + !ExpectBIGNUMsEqual(t, "Sum -u A (r is a)", b.get(), ret.get()) || + !BN_copy(ret.get(), a.get()) || + !BN_usub(ret.get(), sum.get(), ret.get()) || + !ExpectBIGNUMsEqual(t, "Sum -u A (r is b)", b.get(), ret.get()) || + !BN_copy(ret.get(), sum.get()) || + !BN_usub(ret.get(), ret.get(), b.get()) || + !ExpectBIGNUMsEqual(t, "Sum -u B (r is a)", a.get(), ret.get()) || + !BN_copy(ret.get(), b.get()) || + !BN_usub(ret.get(), sum.get(), ret.get()) || + !ExpectBIGNUMsEqual(t, "Sum -u B (r is b)", a.get(), ret.get())) { + return false; + } + } + // Test with |BN_add_word| and |BN_sub_word| if |b| is small enough. BN_ULONG b_word = BN_get_word(b.get()); if (!BN_is_negative(b.get()) && b_word != (BN_ULONG)-1) { diff --git a/include/openssl/bn.h b/include/openssl/bn.h index bc715b02f..23dd8073f 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -388,7 +388,7 @@ OPENSSL_EXPORT int BN_add_word(BIGNUM *a, BN_ULONG w); OPENSSL_EXPORT int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); /* BN_usub sets |r| = |a| - |b|, where |a| and |b| are non-negative integers, - * |b| < |a| and |r| must be a distinct pointer from |a| and |b|. It returns + * |b| < |a| and |r| may be the same pointer as either |a| or |b|. It returns * one on success and zero on allocation failure. */ OPENSSL_EXPORT int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); From 60edffd2a59a7bb5da88735097d41a827ce8074c Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Tue, 21 Jun 2016 15:19:24 -0700 Subject: [PATCH 14/40] Change SignatureAndHashAlgorithm to SignatureScheme in Go. TLS 1.3 defines a new SignatureScheme uint16 enum that is backwards compatible on the wire with TLS1.2's SignatureAndHashAlgorithm. This change updates the go testing code to use a single signatureAlgorithm enum (instead of 2 separate signature and hash enums) in preparation for TLS 1.3. It also unifies all the signing around this new scheme, effectively backporting the change to TLS 1.2. For now, it does not distinguish signature algorithms between 1.2 and 1.3 (RSA-PSS instead of RSA-PKCS1, ECDSA must match curve types). When the C code is ready make a similar change, the Go code will be updated to match. [Originally written by nharper, tweaked significantly by davidben.] Change-Id: If9a315c4670755089ac061e4ec254ef3457a00de Reviewed-on: https://boringssl-review.googlesource.com/8450 Reviewed-by: David Benjamin --- ssl/test/bssl_shim.cc | 12 +- ssl/test/runner/cipher_suites.go | 14 +- ssl/test/runner/common.go | 92 ++++---- ssl/test/runner/conn.go | 14 +- ssl/test/runner/handshake_client.go | 76 ++++--- ssl/test/runner/handshake_messages.go | 89 ++++---- ssl/test/runner/handshake_server.go | 78 ++----- ssl/test/runner/key_agreement.go | 291 +++++++++----------------- ssl/test/runner/prf.go | 75 +------ ssl/test/runner/runner.go | 245 ++++++++++++++-------- ssl/test/runner/sign.go | 219 +++++++++++++++++++ ssl/test/test_config.cc | 4 +- ssl/test/test_config.h | 2 +- 13 files changed, 666 insertions(+), 545 deletions(-) create mode 100644 ssl/test/runner/sign.go diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 851697731..54157ee11 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc @@ -1128,12 +1128,12 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) { } } - if (config->expect_server_key_exchange_hash != 0 && - config->expect_server_key_exchange_hash != - SSL_get_server_key_exchange_hash(ssl)) { - fprintf(stderr, "ServerKeyExchange hash was %d, wanted %d.\n", - SSL_get_server_key_exchange_hash(ssl), - config->expect_server_key_exchange_hash); + if (config->expect_peer_signature_algorithm != 0 && + config->expect_peer_signature_algorithm != + SSL_get_peer_signature_algorithm(ssl)) { + fprintf(stderr, "Peer signature algorithm was %04x, wanted %04x.\n", + SSL_get_peer_signature_algorithm(ssl), + config->expect_peer_signature_algorithm); return false; } diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go index 8c5be3dec..ad2a6380e 100644 --- a/ssl/test/runner/cipher_suites.go +++ b/ssl/test/runner/cipher_suites.go @@ -35,6 +35,10 @@ type keyAgreement interface { // ServerKeyExchange message. processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) + + // peerSignatureAlgorithm returns the signature algorithm used by the + // peer, or zero if not applicable. + peerSignatureAlgorithm() signatureAlgorithm } const ( @@ -406,7 +410,7 @@ func rsaKA(version uint16) keyAgreement { func ecdheECDSAKA(version uint16) keyAgreement { return &ecdheKeyAgreement{ auth: &signedKeyAgreement{ - sigType: signatureECDSA, + keyType: keyTypeECDSA, version: version, }, } @@ -415,7 +419,7 @@ func ecdheECDSAKA(version uint16) keyAgreement { func cecpq1ECDSAKA(version uint16) keyAgreement { return &cecpq1KeyAgreement{ auth: &signedKeyAgreement{ - sigType: signatureECDSA, + keyType: keyTypeECDSA, version: version, }, } @@ -424,7 +428,7 @@ func cecpq1ECDSAKA(version uint16) keyAgreement { func ecdheRSAKA(version uint16) keyAgreement { return &ecdheKeyAgreement{ auth: &signedKeyAgreement{ - sigType: signatureRSA, + keyType: keyTypeRSA, version: version, }, } @@ -433,7 +437,7 @@ func ecdheRSAKA(version uint16) keyAgreement { func cecpq1RSAKA(version uint16) keyAgreement { return &cecpq1KeyAgreement{ auth: &signedKeyAgreement{ - sigType: signatureRSA, + keyType: keyTypeRSA, version: version, }, } @@ -442,7 +446,7 @@ func cecpq1RSAKA(version uint16) keyAgreement { func dheRSAKA(version uint16) keyAgreement { return &dheKeyAgreement{ auth: &signedKeyAgreement{ - sigType: signatureRSA, + keyType: keyTypeRSA, version: version, }, } diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index c9593fff1..56a7c4982 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -132,43 +132,51 @@ const ( // Rest of these are reserved by the TLS spec ) -// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1) -const ( - hashMD5 uint8 = 1 - hashSHA1 uint8 = 2 - hashSHA224 uint8 = 3 - hashSHA256 uint8 = 4 - hashSHA384 uint8 = 5 - hashSHA512 uint8 = 6 -) +// signatureAlgorithm corresponds to a SignatureScheme value from TLS 1.3. Note +// that TLS 1.3 names the production 'SignatureScheme' to avoid colliding with +// TLS 1.2's SignatureAlgorithm but otherwise refers to them as 'signature +// algorithms' throughout. We match the latter. +type signatureAlgorithm uint16 -// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1) const ( - signatureRSA uint8 = 1 - signatureECDSA uint8 = 3 -) + // RSASSA-PKCS1-v1_5 algorithms + signatureRSAPKCS1WithMD5 signatureAlgorithm = 0x0101 + signatureRSAPKCS1WithSHA1 signatureAlgorithm = 0x0201 + signatureRSAPKCS1WithSHA256 signatureAlgorithm = 0x0401 + signatureRSAPKCS1WithSHA384 signatureAlgorithm = 0x0501 + signatureRSAPKCS1WithSHA512 signatureAlgorithm = 0x0601 -// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See -// RFC 5246, section A.4.1. -type signatureAndHash struct { - signature, hash uint8 -} + // ECDSA algorithms + signatureECDSAWithSHA1 signatureAlgorithm = 0x0203 + signatureECDSAWithP256AndSHA256 signatureAlgorithm = 0x0403 + signatureECDSAWithP384AndSHA384 signatureAlgorithm = 0x0503 + signatureECDSAWithP521AndSHA512 signatureAlgorithm = 0x0603 + + // RSASSA-PSS algorithms + signatureRSAPSSWithSHA256 signatureAlgorithm = 0x0700 + signatureRSAPSSWithSHA384 signatureAlgorithm = 0x0701 + signatureRSAPSSWithSHA512 signatureAlgorithm = 0x0702 + + // EdDSA algorithms + signatureEd25519 signatureAlgorithm = 0x0703 + signatureEd448 signatureAlgorithm = 0x0704 +) // supportedSKXSignatureAlgorithms contains the signature and hash algorithms // that the code advertises as supported in a TLS 1.2 ClientHello. -var supportedSKXSignatureAlgorithms = []signatureAndHash{ - {signatureRSA, hashSHA256}, - {signatureECDSA, hashSHA256}, - {signatureRSA, hashSHA1}, - {signatureECDSA, hashSHA1}, +var supportedSKXSignatureAlgorithms = []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + signatureECDSAWithP256AndSHA256, + signatureRSAPKCS1WithSHA1, + signatureECDSAWithSHA1, } -// supportedClientCertSignatureAlgorithms contains the signature and hash +// supportedPeerSignatureAlgorithms contains the signature and hash // algorithms that the code advertises as supported in a TLS 1.2 // CertificateRequest. -var supportedClientCertSignatureAlgorithms = []signatureAndHash{ - {signatureRSA, hashSHA256}, - {signatureECDSA, hashSHA256}, +var supportedPeerSignatureAlgorithms = []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + signatureECDSAWithP256AndSHA256, } // SRTP protection profiles (See RFC 5764, section 4.1.2) @@ -193,7 +201,7 @@ type ConnectionState struct { SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile TLSUnique []byte // the tls-unique channel binding SCTList []byte // signed certificate timestamp list - ClientCertSignatureHash uint8 // TLS id of the hash used by the client to sign the handshake + PeerSignatureAlgorithm signatureAlgorithm // algorithm used by the peer in the handshake } // ClientAuthType declares the policy the server will follow for @@ -378,10 +386,10 @@ type Config struct { // protection profiles to offer in DTLS-SRTP. SRTPProtectionProfiles []uint16 - // SignatureAndHashes, if not nil, overrides the default set of + // SignatureAlgorithms, if not nil, overrides the default set of // supported signature and hash algorithms to advertise in // CertificateRequest. - SignatureAndHashes []signatureAndHash + SignatureAlgorithms []signatureAlgorithm // Bugs specifies optional misbehaviour to be used for testing other // implementations. @@ -640,13 +648,13 @@ type ProtocolBugs struct { // server sends in the ServerHello instead of the negotiated one. SendSRTPProtectionProfile uint16 - // NoSignatureAndHashes, if true, causes the client to omit the + // NoSignatureAlgorithms, if true, causes the client to omit the // signature and hashes extension. // // For a server, it will cause an empty list to be sent in the // CertificateRequest message. None the less, the configured set will // still be enforced. - NoSignatureAndHashes bool + NoSignatureAlgorithms bool // NoSupportedCurves, if true, causes the client to omit the // supported_curves extension. @@ -980,16 +988,16 @@ func (c *Config) getCertificateForName(name string) *Certificate { return &c.Certificates[0] } -func (c *Config) signatureAndHashesForServer() []signatureAndHash { - if c != nil && c.SignatureAndHashes != nil { - return c.SignatureAndHashes +func (c *Config) signatureAlgorithmsForServer() []signatureAlgorithm { + if c != nil && c.SignatureAlgorithms != nil { + return c.SignatureAlgorithms } - return supportedClientCertSignatureAlgorithms + return supportedPeerSignatureAlgorithms } -func (c *Config) signatureAndHashesForClient() []signatureAndHash { - if c != nil && c.SignatureAndHashes != nil { - return c.SignatureAndHashes +func (c *Config) signatureAlgorithmsForClient() []signatureAlgorithm { + if c != nil && c.SignatureAlgorithms != nil { + return c.SignatureAlgorithms } return supportedSKXSignatureAlgorithms } @@ -1206,9 +1214,9 @@ func unexpectedMessageError(wanted, got interface{}) error { return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted) } -func isSupportedSignatureAndHash(sigHash signatureAndHash, sigHashes []signatureAndHash) bool { - for _, s := range sigHashes { - if s == sigHash { +func isSupportedSignatureAlgorithm(sigAlg signatureAlgorithm, sigAlgs []signatureAlgorithm) bool { + for _, s := range sigAlgs { + if s == sigAlg { return true } } diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index e8f685752..8244a4c1c 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go @@ -50,11 +50,9 @@ type Conn struct { // firstFinished contains the first Finished hash sent during the // handshake. This is the "tls-unique" channel binding value. firstFinished [12]byte - // clientCertSignatureHash contains the TLS hash id for the hash that - // was used by the client to sign the handshake with a client - // certificate. This is only set by a server and is zero if no client - // certificates were used. - clientCertSignatureHash uint8 + // peerSignatureAlgorithm contains the signature algorithm that was used + // by the peer in the handshake, or zero if not applicable. + peerSignatureAlgorithm signatureAlgorithm clientRandom, serverRandom [32]byte masterSecret [48]byte @@ -1089,7 +1087,7 @@ func (c *Conn) readHandshake() (interface{}, error) { m = new(certificateMsg) case typeCertificateRequest: m = &certificateRequestMsg{ - hasSignatureAndHash: c.vers >= VersionTLS12, + hasSignatureAlgorithm: c.vers >= VersionTLS12, } case typeCertificateStatus: m = new(certificateStatusMsg) @@ -1101,7 +1099,7 @@ func (c *Conn) readHandshake() (interface{}, error) { m = new(clientKeyExchangeMsg) case typeCertificateVerify: m = &certificateVerifyMsg{ - hasSignatureAndHash: c.vers >= VersionTLS12, + hasSignatureAlgorithm: c.vers >= VersionTLS12, } case typeNextProtocol: m = new(nextProtoMsg) @@ -1436,7 +1434,7 @@ func (c *Conn) ConnectionState() ConnectionState { state.SRTPProtectionProfile = c.srtpProtectionProfile state.TLSUnique = c.firstFinished[:] state.SCTList = c.sctList - state.ClientCertSignatureHash = c.clientCertSignatureHash + state.PeerSignatureAlgorithm = c.peerSignatureAlgorithm } return state diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 5123f556a..62154b82a 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -6,12 +6,12 @@ package runner import ( "bytes" + "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" "crypto/subtle" "crypto/x509" - "encoding/asn1" "errors" "fmt" "io" @@ -141,8 +141,8 @@ NextCipherSuite: return errors.New("tls: short read from Rand: " + err.Error()) } - if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes { - hello.signatureAndHashes = c.config.signatureAndHashesForClient() + if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAlgorithms { + hello.signatureAlgorithms = c.config.signatureAlgorithmsForClient() } var session *ClientSessionState @@ -474,6 +474,8 @@ func (hs *clientHandshakeState) doFullHandshake() error { return err } + c.peerSignatureAlgorithm = keyAgreement.peerSignatureAlgorithm() + msg, err = c.readHandshake() if err != nil { return err @@ -605,56 +607,50 @@ func (hs *clientHandshakeState) doFullHandshake() error { } if chainToSend != nil { - var signed []byte certVerify := &certificateVerifyMsg{ - hasSignatureAndHash: c.vers >= VersionTLS12, + hasSignatureAlgorithm: c.vers >= VersionTLS12, } // Determine the hash to sign. - var signatureType uint8 - switch c.config.Certificates[0].PrivateKey.(type) { - case *ecdsa.PrivateKey: - signatureType = signatureECDSA - case *rsa.PrivateKey: - signatureType = signatureRSA - default: - c.sendAlert(alertInternalError) - return errors.New("unknown private key type") - } + privKey := c.config.Certificates[0].PrivateKey if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences { - certReq.signatureAndHashes = c.config.signatureAndHashesForClient() - } - certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, c.config.signatureAndHashesForClient(), signatureType) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - digest, hashFunc, err := hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - if c.config.Bugs.InvalidCertVerifySignature { - digest[0] ^= 0x80 + certReq.signatureAlgorithms = c.config.signatureAlgorithmsForClient() } - switch key := c.config.Certificates[0].PrivateKey.(type) { - case *ecdsa.PrivateKey: - var r, s *big.Int - r, s, err = ecdsa.Sign(c.config.rand(), key, digest) - if err == nil { - signed, err = asn1.Marshal(ecdsaSignature{r, s}) + if certVerify.hasSignatureAlgorithm { + certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, certReq.signatureAlgorithms, c.config.signatureAlgorithmsForClient()) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + } + + if c.vers > VersionSSL30 { + msg := hs.finishedHash.buffer + if c.config.Bugs.InvalidCertVerifySignature { + msg = make([]byte, len(hs.finishedHash.buffer)) + copy(msg, hs.finishedHash.buffer) + msg[0] ^= 0x80 + } + certVerify.signature, err = signMessage(c.vers, privKey, c.config, certVerify.signatureAlgorithm, msg) + } else { + // SSL 3.0's client certificate construction is + // incompatible with signatureAlgorithm. + rsaKey, ok := privKey.(*rsa.PrivateKey) + if !ok { + err = errors.New("unsupported signature type for client certificate") + } else { + digest := hs.finishedHash.hashForClientCertificateSSL3(hs.masterSecret) + if c.config.Bugs.InvalidCertVerifySignature { + digest[0] ^= 0x80 + } + certVerify.signature, err = rsa.SignPKCS1v15(c.config.rand(), rsaKey, crypto.MD5SHA1, digest) } - case *rsa.PrivateKey: - signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest) - default: - err = errors.New("unknown private key type") } if err != nil { c.sendAlert(alertInternalError) return errors.New("tls: failed to sign handshake with client certificate: " + err.Error()) } - certVerify.signature = signed hs.writeClientHash(certVerify.marshal()) c.writeRecord(recordTypeHandshake, certVerify.marshal()) diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 092f51ef4..9637e9a09 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -22,7 +22,7 @@ type clientHelloMsg struct { supportedPoints []uint8 ticketSupported bool sessionTicket []uint8 - signatureAndHashes []signatureAndHash + signatureAlgorithms []signatureAlgorithm secureRenegotiation []byte alpnProtocols []string duplicateExtension bool @@ -56,7 +56,7 @@ func (m *clientHelloMsg) equal(i interface{}) bool { bytes.Equal(m.supportedPoints, m1.supportedPoints) && m.ticketSupported == m1.ticketSupported && bytes.Equal(m.sessionTicket, m1.sessionTicket) && - eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) && + eqSignatureAlgorithms(m.signatureAlgorithms, m1.signatureAlgorithms) && bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && (m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) && eqStrings(m.alpnProtocols, m1.alpnProtocols) && @@ -104,8 +104,8 @@ func (m *clientHelloMsg) marshal() []byte { extensionsLength += len(m.sessionTicket) numExtensions++ } - if len(m.signatureAndHashes) > 0 { - extensionsLength += 2 + 2*len(m.signatureAndHashes) + if len(m.signatureAlgorithms) > 0 { + extensionsLength += 2 + 2*len(m.signatureAlgorithms) numExtensions++ } if m.secureRenegotiation != nil { @@ -281,11 +281,11 @@ func (m *clientHelloMsg) marshal() []byte { copy(z, m.sessionTicket) z = z[len(m.sessionTicket):] } - if len(m.signatureAndHashes) > 0 { + if len(m.signatureAlgorithms) > 0 { // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 z[0] = byte(extensionSignatureAlgorithms >> 8) z[1] = byte(extensionSignatureAlgorithms) - l := 2 + 2*len(m.signatureAndHashes) + l := 2 + 2*len(m.signatureAlgorithms) z[2] = byte(l >> 8) z[3] = byte(l) z = z[4:] @@ -294,9 +294,9 @@ func (m *clientHelloMsg) marshal() []byte { z[0] = byte(l >> 8) z[1] = byte(l) z = z[2:] - for _, sigAndHash := range m.signatureAndHashes { - z[0] = sigAndHash.hash - z[1] = sigAndHash.signature + for _, sigAlg := range m.signatureAlgorithms { + z[0] = byte(sigAlg >> 8) + z[1] = byte(sigAlg) z = z[2:] } } @@ -454,7 +454,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.ocspStapling = false m.ticketSupported = false m.sessionTicket = nil - m.signatureAndHashes = nil + m.signatureAlgorithms = nil m.alpnProtocols = nil m.extendedMasterSecret = false m.customExtension = "" @@ -556,10 +556,9 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } n := l / 2 d := data[2:] - m.signatureAndHashes = make([]signatureAndHash, n) - for i := range m.signatureAndHashes { - m.signatureAndHashes[i].hash = d[0] - m.signatureAndHashes[i].signature = d[1] + m.signatureAlgorithms = make([]signatureAlgorithm, n) + for i := range m.signatureAlgorithms { + m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1]) d = d[2:] } case extensionRenegotiationInfo: @@ -1296,13 +1295,13 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool { type certificateRequestMsg struct { raw []byte - // hasSignatureAndHash indicates whether this message includes a list + // hasSignatureAlgorithm indicates whether this message includes a list // of signature and hash functions. This change was introduced with TLS // 1.2. - hasSignatureAndHash bool + hasSignatureAlgorithm bool certificateTypes []byte - signatureAndHashes []signatureAndHash + signatureAlgorithms []signatureAlgorithm certificateAuthorities [][]byte } @@ -1319,8 +1318,8 @@ func (m *certificateRequestMsg) marshal() (x []byte) { } length += casLength - if m.hasSignatureAndHash { - length += 2 + 2*len(m.signatureAndHashes) + if m.hasSignatureAlgorithm { + length += 2 + 2*len(m.signatureAlgorithms) } x = make([]byte, 4+length) @@ -1334,14 +1333,14 @@ func (m *certificateRequestMsg) marshal() (x []byte) { copy(x[5:], m.certificateTypes) y := x[5+len(m.certificateTypes):] - if m.hasSignatureAndHash { - n := len(m.signatureAndHashes) * 2 + if m.hasSignatureAlgorithm { + n := len(m.signatureAlgorithms) * 2 y[0] = uint8(n >> 8) y[1] = uint8(n) y = y[2:] - for _, sigAndHash := range m.signatureAndHashes { - y[0] = sigAndHash.hash - y[1] = sigAndHash.signature + for _, sigAlg := range m.signatureAlgorithms { + y[0] = byte(sigAlg >> 8) + y[1] = byte(sigAlg) y = y[2:] } } @@ -1386,23 +1385,22 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { data = data[numCertTypes:] - if m.hasSignatureAndHash { + if m.hasSignatureAlgorithm { if len(data) < 2 { return false } - sigAndHashLen := uint16(data[0])<<8 | uint16(data[1]) + sigAlgsLen := uint16(data[0])<<8 | uint16(data[1]) data = data[2:] - if sigAndHashLen&1 != 0 { + if sigAlgsLen&1 != 0 { return false } - if len(data) < int(sigAndHashLen) { + if len(data) < int(sigAlgsLen) { return false } - numSigAndHash := sigAndHashLen / 2 - m.signatureAndHashes = make([]signatureAndHash, numSigAndHash) - for i := range m.signatureAndHashes { - m.signatureAndHashes[i].hash = data[0] - m.signatureAndHashes[i].signature = data[1] + numSigAlgs := sigAlgsLen / 2 + m.signatureAlgorithms = make([]signatureAlgorithm, numSigAlgs) + for i := range m.signatureAlgorithms { + m.signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1]) data = data[2:] } } @@ -1442,10 +1440,10 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { } type certificateVerifyMsg struct { - raw []byte - hasSignatureAndHash bool - signatureAndHash signatureAndHash - signature []byte + raw []byte + hasSignatureAlgorithm bool + signatureAlgorithm signatureAlgorithm + signature []byte } func (m *certificateVerifyMsg) marshal() (x []byte) { @@ -1456,7 +1454,7 @@ func (m *certificateVerifyMsg) marshal() (x []byte) { // See http://tools.ietf.org/html/rfc4346#section-7.4.8 siglength := len(m.signature) length := 2 + siglength - if m.hasSignatureAndHash { + if m.hasSignatureAlgorithm { length += 2 } x = make([]byte, 4+length) @@ -1465,9 +1463,9 @@ func (m *certificateVerifyMsg) marshal() (x []byte) { x[2] = uint8(length >> 8) x[3] = uint8(length) y := x[4:] - if m.hasSignatureAndHash { - y[0] = m.signatureAndHash.hash - y[1] = m.signatureAndHash.signature + if m.hasSignatureAlgorithm { + y[0] = byte(m.signatureAlgorithm >> 8) + y[1] = byte(m.signatureAlgorithm) y = y[2:] } y[0] = uint8(siglength >> 8) @@ -1492,9 +1490,8 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { } data = data[4:] - if m.hasSignatureAndHash { - m.signatureAndHash.hash = data[0] - m.signatureAndHash.signature = data[1] + if m.hasSignatureAlgorithm { + m.signatureAlgorithm = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1]) data = data[2:] } @@ -1745,13 +1742,13 @@ func eqByteSlices(x, y [][]byte) bool { return true } -func eqSignatureAndHashes(x, y []signatureAndHash) bool { +func eqSignatureAlgorithms(x, y []signatureAlgorithm) bool { if len(x) != len(y) { return false } for i, v := range x { v2 := y[i] - if v.hash != v2.hash || v.signature != v2.signature { + if v != v2 { return false } } diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 7d462b55d..7847acb5f 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -12,7 +12,6 @@ import ( "crypto/rsa" "crypto/subtle" "crypto/x509" - "encoding/asn1" "errors" "fmt" "io" @@ -196,11 +195,11 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { c.clientVersion = hs.clientHello.vers // Reject < 1.2 ClientHellos with signature_algorithms. - if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAndHashes) > 0 { + if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 { return false, fmt.Errorf("tls: client included signature_algorithms before TLS 1.2") } if config.Bugs.IgnorePeerSignatureAlgorithmPreferences { - hs.clientHello.signatureAndHashes = config.signatureAndHashesForServer() + hs.clientHello.signatureAlgorithms = config.signatureAlgorithmsForServer() } // Check the client cipher list is consistent with the version. @@ -592,9 +591,9 @@ func (hs *serverHandshakeState) doFullHandshake() error { } } if c.vers >= VersionTLS12 { - certReq.hasSignatureAndHash = true - if !config.Bugs.NoSignatureAndHashes { - certReq.signatureAndHashes = config.signatureAndHashesForServer() + certReq.hasSignatureAlgorithm = true + if !config.Bugs.NoSignatureAlgorithms { + certReq.signatureAlgorithms = config.signatureAlgorithmsForServer() } } @@ -707,60 +706,27 @@ func (hs *serverHandshakeState) doFullHandshake() error { } // Determine the signature type. - var signatureAndHash signatureAndHash - if certVerify.hasSignatureAndHash { - signatureAndHash = certVerify.signatureAndHash - if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) { - return errors.New("tls: unsupported hash function for client certificate") - } - c.clientCertSignatureHash = signatureAndHash.hash - } else { - // Before TLS 1.2 the signature algorithm was implicit - // from the key type, and only one hash per signature - // algorithm was possible. Leave the hash as zero. - switch pub.(type) { - case *ecdsa.PublicKey: - signatureAndHash.signature = signatureECDSA - case *rsa.PublicKey: - signatureAndHash.signature = signatureRSA + var sigAlg signatureAlgorithm + if certVerify.hasSignatureAlgorithm { + sigAlg = certVerify.signatureAlgorithm + if !isSupportedSignatureAlgorithm(sigAlg, config.signatureAlgorithmsForServer()) { + return errors.New("tls: unsupported signature algorithm for client certificate") } + c.peerSignatureAlgorithm = sigAlg } - switch key := pub.(type) { - case *ecdsa.PublicKey: - if signatureAndHash.signature != signatureECDSA { - err = errors.New("tls: bad signature type for client's ECDSA certificate") - break + if c.vers > VersionSSL30 { + err = verifyMessage(c.vers, pub, sigAlg, hs.finishedHash.buffer, certVerify.signature) + } else { + // SSL 3.0's client certificate construction is + // incompatible with signatureAlgorithm. + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + err = errors.New("unsupported key type for client certificate") + } else { + digest := hs.finishedHash.hashForClientCertificateSSL3(hs.masterSecret) + err = rsa.VerifyPKCS1v15(rsaPub, crypto.MD5SHA1, digest, certVerify.signature) } - ecdsaSig := new(ecdsaSignature) - if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil { - break - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - err = errors.New("ECDSA signature contained zero or negative values") - break - } - var digest []byte - digest, _, err = hs.finishedHash.hashForClientCertificate(signatureAndHash, hs.masterSecret) - if err != nil { - break - } - if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) { - err = errors.New("ECDSA verification failure") - break - } - case *rsa.PublicKey: - if signatureAndHash.signature != signatureRSA { - err = errors.New("tls: bad signature type for client's RSA certificate") - break - } - var digest []byte - var hashFunc crypto.Hash - digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(signatureAndHash, hs.masterSecret) - if err != nil { - break - } - err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature) } if err != nil { c.sendAlert(alertBadCertificate) diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go index 5e5d976d0..cb02c53a1 100644 --- a/ssl/test/runner/key_agreement.go +++ b/ssl/test/runner/key_agreement.go @@ -5,16 +5,12 @@ package runner import ( - "crypto" "crypto/ecdsa" "crypto/elliptic" - "crypto/md5" "crypto/rand" "crypto/rsa" - "crypto/sha1" "crypto/subtle" "crypto/x509" - "encoding/asn1" "errors" "fmt" "io" @@ -24,6 +20,13 @@ import ( "./newhope" ) +type keyType int + +const ( + keyTypeRSA keyType = iota + 1 + keyTypeECDSA +) + var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message") @@ -59,37 +62,30 @@ func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certi serverRSAParams = append(serverRSAParams, byte(len(exponent)>>8), byte(len(exponent))) serverRSAParams = append(serverRSAParams, exponent...) - var tls12HashId uint8 + var sigAlg signatureAlgorithm if ka.version >= VersionTLS12 { - if tls12HashId, err = pickTLS12HashForSignature(signatureRSA, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil { + sigAlg, err = selectSignatureAlgorithm(ka.version, cert.PrivateKey, clientHello.signatureAlgorithms, config.signatureAlgorithmsForServer()) + if err != nil { return nil, err } } - digest, hashFunc, err := hashForServerKeyExchange(signatureRSA, tls12HashId, ka.version, clientHello.random, hello.random, serverRSAParams) - if err != nil { - return nil, err - } - privKey, ok := cert.PrivateKey.(*rsa.PrivateKey) - if !ok { - return nil, errors.New("RSA ephemeral key requires an RSA server private key") - } - sig, err := rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest) + sig, err := signMessage(ka.version, cert.PrivateKey, config, sigAlg, serverRSAParams) if err != nil { return nil, errors.New("failed to sign RSA parameters: " + err.Error()) } skx := new(serverKeyExchangeMsg) - sigAndHashLen := 0 + sigAlgsLen := 0 if ka.version >= VersionTLS12 { - sigAndHashLen = 2 + sigAlgsLen = 2 } - skx.key = make([]byte, len(serverRSAParams)+sigAndHashLen+2+len(sig)) + skx.key = make([]byte, len(serverRSAParams)+sigAlgsLen+2+len(sig)) copy(skx.key, serverRSAParams) k := skx.key[len(serverRSAParams):] if ka.version >= VersionTLS12 { - k[0] = tls12HashId - k[1] = signatureRSA + k[0] = byte(sigAlg >> 8) + k[1] = byte(sigAlg) k = k[2:] } k[0] = byte(len(sig) >> 8) @@ -185,70 +181,8 @@ func (ka *rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello return preMasterSecret, ckx, nil } -// sha1Hash calculates a SHA1 hash over the given byte slices. -func sha1Hash(slices [][]byte) []byte { - hsha1 := sha1.New() - for _, slice := range slices { - hsha1.Write(slice) - } - return hsha1.Sum(nil) -} - -// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the -// concatenation of an MD5 and SHA1 hash. -func md5SHA1Hash(slices [][]byte) []byte { - md5sha1 := make([]byte, md5.Size+sha1.Size) - hmd5 := md5.New() - for _, slice := range slices { - hmd5.Write(slice) - } - copy(md5sha1, hmd5.Sum(nil)) - copy(md5sha1[md5.Size:], sha1Hash(slices)) - return md5sha1 -} - -// hashForServerKeyExchange hashes the given slices and returns their digest -// and the identifier of the hash function used. The hashFunc argument is only -// used for >= TLS 1.2 and precisely identifies the hash function to use. -func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) { - if version >= VersionTLS12 { - hash, err := lookupTLSHash(hashFunc) - if err != nil { - return nil, 0, err - } - h := hash.New() - for _, slice := range slices { - h.Write(slice) - } - return h.Sum(nil), hash, nil - } - if sigType == signatureECDSA { - return sha1Hash(slices), crypto.SHA1, nil - } - return md5SHA1Hash(slices), crypto.MD5SHA1, nil -} - -// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a -// ServerKeyExchange given the signature type being used and the client's -// advertized list of supported signature and hash combinations. -func pickTLS12HashForSignature(sigType uint8, clientList, serverList []signatureAndHash) (uint8, error) { - if len(clientList) == 0 { - // If the client didn't specify any signature_algorithms - // extension then we can assume that it supports SHA1. See - // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - return hashSHA1, nil - } - - for _, sigAndHash := range clientList { - if sigAndHash.signature != sigType { - continue - } - if isSupportedSignatureAndHash(sigAndHash, serverList) { - return sigAndHash.hash, nil - } - } - - return 0, errors.New("tls: client doesn't support any common hash functions") +func (ka *rsaKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + return 0 } // A ecdhCurve is an instance of ECDH-style key agreement for TLS. @@ -448,91 +382,53 @@ func (ka *nilKeyAgreementAuthentication) verifyParameters(config *Config, client return nil } -func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int { - switch typeOfCorruption { - case BadValueNone: - return n - case BadValueNegative: - return new(big.Int).Neg(n) - case BadValueZero: - return big.NewInt(0) - case BadValueLimit: - return limit - case BadValueLarge: - bad := new(big.Int).Set(limit) - return bad.Lsh(bad, 20) - default: - panic("unknown BadValue type") - } -} - // signedKeyAgreement signs the ServerKeyExchange parameters with the // server's private key. type signedKeyAgreement struct { - version uint16 - sigType uint8 + keyType keyType + version uint16 + peerSignatureAlgorithm signatureAlgorithm } func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) { - var tls12HashId uint8 + // The message to be signed is prepended by the randoms. + var msg []byte + msg = append(msg, clientHello.random...) + msg = append(msg, hello.random...) + msg = append(msg, params...) + + if config.Bugs.InvalidSKXSignature { + msg[0] ^= 0x80 + } + + var sigAlg signatureAlgorithm var err error if ka.version >= VersionTLS12 { - if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes, config.signatureAndHashesForServer()); err != nil { + sigAlg, err = selectSignatureAlgorithm(ka.version, cert.PrivateKey, clientHello.signatureAlgorithms, config.signatureAlgorithmsForServer()) + if err != nil { return nil, err } } - digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, params) + sig, err := signMessage(ka.version, cert.PrivateKey, config, sigAlg, msg) if err != nil { return nil, err } - if config.Bugs.InvalidSKXSignature { - digest[0] ^= 0x80 - } - - var sig []byte - switch ka.sigType { - case signatureECDSA: - privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey) - if !ok { - return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key") - } - r, s, err := ecdsa.Sign(config.rand(), privKey, digest) - if err != nil { - return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) - } - order := privKey.Curve.Params().N - r = maybeCorruptECDSAValue(r, config.Bugs.BadECDSAR, order) - s = maybeCorruptECDSAValue(s, config.Bugs.BadECDSAS, order) - sig, err = asn1.Marshal(ecdsaSignature{r, s}) - case signatureRSA: - privKey, ok := cert.PrivateKey.(*rsa.PrivateKey) - if !ok { - return nil, errors.New("ECDHE RSA requires a RSA server private key") - } - sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest) - if err != nil { - return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) - } - default: - return nil, errors.New("unknown ECDHE signature algorithm") - } - skx := new(serverKeyExchangeMsg) if config.Bugs.UnauthenticatedECDH { skx.key = params } else { - sigAndHashLen := 0 + sigAlgsLen := 0 if ka.version >= VersionTLS12 { - sigAndHashLen = 2 + sigAlgsLen = 2 } - skx.key = make([]byte, len(params)+sigAndHashLen+2+len(sig)) + skx.key = make([]byte, len(params)+sigAlgsLen+2+len(sig)) copy(skx.key, params) k := skx.key[len(params):] if ka.version >= VersionTLS12 { - k[0] = tls12HashId - k[1] = ka.sigType + k[0] = byte(sigAlg >> 8) + k[1] = byte(sigAlg) k = k[2:] } k[0] = byte(len(sig) >> 8) @@ -544,26 +440,44 @@ func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, } func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error { - if len(sig) < 2 { - return errServerKeyExchange + // The peer's key must match the cipher type. + switch ka.keyType { + case keyTypeECDSA: + _, ok := cert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("tls: ECDHE ECDSA requires a ECDSA server public key") + } + case keyTypeRSA: + _, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return errors.New("tls: ECDHE RSA requires a RSA server public key") + } + default: + return errors.New("tls: unknown key type") } - var tls12HashId uint8 + // The message to be signed is prepended by the randoms. + var msg []byte + msg = append(msg, clientHello.random...) + msg = append(msg, serverHello.random...) + msg = append(msg, params...) + + var sigAlg signatureAlgorithm if ka.version >= VersionTLS12 { - // handle SignatureAndHashAlgorithm - var sigAndHash []uint8 - sigAndHash, sig = sig[:2], sig[2:] - if sigAndHash[1] != ka.sigType { - return errServerKeyExchange - } - tls12HashId = sigAndHash[0] if len(sig) < 2 { return errServerKeyExchange } - - if !isSupportedSignatureAndHash(signatureAndHash{ka.sigType, tls12HashId}, config.signatureAndHashesForClient()) { - return errors.New("tls: unsupported hash function for ServerKeyExchange") + sigAlg = signatureAlgorithm(sig[0])<<8 | signatureAlgorithm(sig[1]) + sig = sig[2:] + if !isSupportedSignatureAlgorithm(sigAlg, config.signatureAlgorithmsForClient()) { + return errors.New("tls: unsupported signature algorithm for ServerKeyExchange") } + // Stash the signature algorithm to be extracted by the handshake. + ka.peerSignatureAlgorithm = sigAlg + } + + if len(sig) < 2 { + return errServerKeyExchange } sigLen := int(sig[0])<<8 | int(sig[1]) if sigLen+2 != len(sig) { @@ -571,39 +485,7 @@ func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clie } sig = sig[2:] - digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, params) - if err != nil { - return err - } - switch ka.sigType { - case signatureECDSA: - pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey) - if !ok { - return errors.New("ECDHE ECDSA requires a ECDSA server public key") - } - ecdsaSig := new(ecdsaSignature) - if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { - return err - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("ECDSA signature contained zero or negative values") - } - if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("ECDSA verification failure") - } - case signatureRSA: - pubKey, ok := cert.PublicKey.(*rsa.PublicKey) - if !ok { - return errors.New("ECDHE RSA requires a RSA server public key") - } - if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { - return err - } - default: - return errors.New("unknown ECDHE signature algorithm") - } - - return nil + return verifyMessage(ka.version, cert.PublicKey, sigAlg, msg, sig) } // ecdheRSAKeyAgreement implements a TLS key agreement where the server @@ -716,6 +598,13 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel return preMasterSecret, ckx, nil } +func (ka *ecdheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + if auth, ok := ka.auth.(*signedKeyAgreement); ok { + return auth.peerSignatureAlgorithm + } + return 0 +} + // cecpq1RSAKeyAgreement is like an ecdheKeyAgreement, but using the cecpq1Curve // pseudo-curve, and without any parameters (e.g. curve name) other than the // keys being exchanged. The signature may either be ECDSA or RSA. @@ -807,6 +696,13 @@ func (ka *cecpq1KeyAgreement) generateClientKeyExchange(config *Config, clientHe return preMasterSecret, ckx, nil } +func (ka *cecpq1KeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + if auth, ok := ka.auth.(*signedKeyAgreement); ok { + return auth.peerSignatureAlgorithm + } + return 0 +} + // dheRSAKeyAgreement implements a TLS key agreement where the server generates // an ephemeral Diffie-Hellman public/private key pair and signs it. The // pre-master secret is then calculated using Diffie-Hellman. @@ -941,6 +837,13 @@ func (ka *dheKeyAgreement) generateClientKeyExchange(config *Config, clientHello return preMasterSecret, ckx, nil } +func (ka *dheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + if auth, ok := ka.auth.(*signedKeyAgreement); ok { + return auth.peerSignatureAlgorithm + } + return 0 +} + // nilKeyAgreement is a fake key agreement used to implement the plain PSK key // exchange. type nilKeyAgreement struct{} @@ -976,6 +879,10 @@ func (ka *nilKeyAgreement) generateClientKeyExchange(config *Config, clientHello return nil, &clientKeyExchangeMsg{}, nil } +func (ka *nilKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + return 0 +} + // makePSKPremaster formats a PSK pre-master secret based on otherSecret from // the base key exchange and psk. func makePSKPremaster(otherSecret, psk []byte) []byte { @@ -1101,3 +1008,7 @@ func (ka *pskKeyAgreement) generateClientKeyExchange(config *Config, clientHello } return makePSKPremaster(otherSecret, config.PreSharedKey), ckx, nil } + +func (ka *pskKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + return 0 +} diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go index 1bfe84c14..a91a31925 100644 --- a/ssl/test/runner/prf.go +++ b/ssl/test/runner/prf.go @@ -5,13 +5,11 @@ package runner import ( - "crypto" "crypto/hmac" "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" - "errors" "hash" ) @@ -189,27 +187,6 @@ func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clie return } -// lookupTLSHash looks up the corresponding crypto.Hash for a given -// TLS hash identifier. -func lookupTLSHash(hash uint8) (crypto.Hash, error) { - switch hash { - case hashMD5: - return crypto.MD5, nil - case hashSHA1: - return crypto.SHA1, nil - case hashSHA224: - return crypto.SHA224, nil - case hashSHA256: - return crypto.SHA256, nil - case hashSHA384: - return crypto.SHA384, nil - case hashSHA512: - return crypto.SHA512, nil - default: - return 0, errors.New("tls: unsupported hash algorithm") - } -} - func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { if version >= VersionTLS12 { newHash := sha256.New @@ -325,50 +302,14 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte { return out } -// selectClientCertSignatureAlgorithm returns a signatureAndHash to sign a -// client's CertificateVerify with, or an error if none can be found. -func (h finishedHash) selectClientCertSignatureAlgorithm(serverList, clientList []signatureAndHash, sigType uint8) (signatureAndHash, error) { - if h.version < VersionTLS12 { - // Nothing to negotiate before TLS 1.2. - return signatureAndHash{signature: sigType}, nil - } - - for _, v := range serverList { - if v.signature == sigType && isSupportedSignatureAndHash(v, clientList) { - return v, nil - } - } - return signatureAndHash{}, errors.New("tls: no supported signature algorithm found for signing client certificate") -} - -// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash -// id suitable for signing by a TLS client certificate. -func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash, masterSecret []byte) ([]byte, crypto.Hash, error) { - if h.version == VersionSSL30 { - if signatureAndHash.signature != signatureRSA { - return nil, 0, errors.New("tls: unsupported signature type for client certificate") - } - - md5Hash := md5.New() - md5Hash.Write(h.buffer) - sha1Hash := sha1.New() - sha1Hash.Write(h.buffer) - return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil - } - if h.version >= VersionTLS12 { - hashAlg, err := lookupTLSHash(signatureAndHash.hash) - if err != nil { - return nil, 0, err - } - hash := hashAlg.New() - hash.Write(h.buffer) - return hash.Sum(nil), hashAlg, nil - } - if signatureAndHash.signature == signatureECDSA { - return h.server.Sum(nil), crypto.SHA1, nil - } - - return h.Sum(), crypto.MD5SHA1, nil +// hashForClientCertificateSSL3 returns the hash to be signed for client +// certificates in SSL 3.0. +func (h finishedHash) hashForClientCertificateSSL3(masterSecret []byte) []byte { + md5Hash := md5.New() + md5Hash.Write(h.buffer) + sha1Hash := sha1.New() + sha1Hash.Write(h.buffer) + return finishedSum30(md5Hash, sha1Hash, masterSecret, nil) } // hashForChannelID returns the hash to be signed for TLS Channel diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 0ede7bb06..41b886cb3 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -143,6 +143,46 @@ const ( npn = 2 ) +type testCert int + +const ( + testCertRSA testCert = iota + testCertECDSA +) + +func getRunnerCertificate(t testCert) Certificate { + switch t { + case testCertRSA: + return getRSACertificate() + case testCertECDSA: + return getECDSACertificate() + default: + panic("Unknown test certificate") + } +} + +func getShimCertificate(t testCert) string { + switch t { + case testCertRSA: + return rsaCertificateFile + case testCertECDSA: + return ecdsaCertificateFile + default: + panic("Unknown test certificate") + } +} + +func getShimKey(t testCert) string { + switch t { + case testCertRSA: + return rsaKeyFile + case testCertECDSA: + return ecdsaKeyFile + default: + panic("Unknown test certificate") + } +} + type testCase struct { testType testType protocol protocol @@ -181,10 +221,9 @@ type testCase struct { expectedOCSPResponse []uint8 // expectedSCTList, if not nil, is the expected SCT list to be received. expectedSCTList []uint8 - // expectedClientCertSignatureHash, if not zero, is the TLS id of the - // hash function that the client should have used when signing the - // handshake with a client certificate. - expectedClientCertSignatureHash uint8 + // expectedPeerSignatureAlgorithm, if not zero, is the signature + // algorithm that the peer should have used in the handshake. + expectedPeerSignatureAlgorithm signatureAlgorithm // messageLen is the length, in bytes, of the test message that will be // sent. messageLen int @@ -447,8 +486,8 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) er return fmt.Errorf("SCT list mismatch") } - if expected := test.expectedClientCertSignatureHash; expected != 0 && expected != connState.ClientCertSignatureHash { - return fmt.Errorf("expected client to sign handshake with hash %d, but got %d", expected, connState.ClientCertSignatureHash) + if expected := test.expectedPeerSignatureAlgorithm; expected != 0 && expected != connState.PeerSignatureAlgorithm { + return fmt.Errorf("expected peer to use signature algorithm %04x, but got %04x", expected, connState.PeerSignatureAlgorithm) } if test.exportKeyingMaterial > 0 { @@ -646,10 +685,6 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error { panic("expectResumeRejected without resumeSession in " + test.name) } - if test.testType != clientTest && test.expectedClientCertSignatureHash != 0 { - panic("expectedClientCertSignatureHash non-zero with serverTest in " + test.name) - } - listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}}) if err != nil { panic(err) @@ -4510,78 +4545,123 @@ func addDTLSReplayTests() { }) } -var testHashes = []struct { +var testSignatureAlgorithms = []struct { name string - id uint8 + id signatureAlgorithm + cert testCert }{ - {"SHA1", hashSHA1}, - {"SHA256", hashSHA256}, - {"SHA384", hashSHA384}, - {"SHA512", hashSHA512}, + {"RSA-PKCS1-SHA1", signatureRSAPKCS1WithSHA1, testCertRSA}, + {"RSA-PKCS1-SHA256", signatureRSAPKCS1WithSHA256, testCertRSA}, + {"RSA-PKCS1-SHA384", signatureRSAPKCS1WithSHA384, testCertRSA}, + {"RSA-PKCS1-SHA512", signatureRSAPKCS1WithSHA512, testCertRSA}, + {"ECDSA-SHA1", signatureECDSAWithSHA1, testCertECDSA}, + // TODO(davidben): These signature algorithms are paired with a curve in + // TLS 1.3. Test that, in TLS 1.3, the curves must match and, in TLS + // 1.2, mismatches are tolerated. + {"ECDSA-SHA256", signatureECDSAWithP256AndSHA256, testCertECDSA}, + {"ECDSA-SHA384", signatureECDSAWithP384AndSHA384, testCertECDSA}, + {"ECDSA-SHA512", signatureECDSAWithP521AndSHA512, testCertECDSA}, } -func addSigningHashTests() { - // Make sure each hash works. Include some fake hashes in the list and - // ensure they're ignored. - for _, hash := range testHashes { +const fakeSigAlg1 signatureAlgorithm = 0x2a01 +const fakeSigAlg2 signatureAlgorithm = 0xff01 + +func addSignatureAlgorithmTests() { + // Make sure each signature algorithm works. Include some fake values in + // the list and ensure they're ignored. + for _, alg := range testSignatureAlgorithms { testCases = append(testCases, testCase{ - name: "SigningHash-ClientAuth-" + hash.name, + name: "SigningHash-ClientAuth-Sign-" + alg.name, config: Config{ - ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, 42}, - {signatureRSA, hash.id}, - {signatureRSA, 255}, + // SignatureAlgorithms is shared, so we must + // configure a matching server certificate too. + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + ClientAuth: RequireAnyClientCert, + SignatureAlgorithms: []signatureAlgorithm{ + fakeSigAlg1, + alg.id, + fakeSigAlg2, }, }, flags: []string{ - "-cert-file", path.Join(*resourceDir, rsaCertificateFile), - "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), + }, + expectedPeerSignatureAlgorithm: alg.id, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SigningHash-ClientAuth-Verify-" + alg.name, + config: Config{ + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + SignatureAlgorithms: []signatureAlgorithm{ + alg.id, + }, + }, + flags: []string{ + "-require-any-client-certificate", + "-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)), + // SignatureAlgorithms is shared, so we must + // configure a matching server certificate too. + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), }, }) testCases = append(testCases, testCase{ testType: serverTest, - name: "SigningHash-ServerKeyExchange-Sign-" + hash.name, + name: "SigningHash-ServerKeyExchange-Sign-" + alg.name, config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, 42}, - {signatureRSA, hash.id}, - {signatureRSA, 255}, + CipherSuites: []uint16{ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + SignatureAlgorithms: []signatureAlgorithm{ + fakeSigAlg1, + alg.id, + fakeSigAlg2, }, }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), + }, + expectedPeerSignatureAlgorithm: alg.id, }) testCases = append(testCases, testCase{ - name: "SigningHash-ServerKeyExchange-Verify-" + hash.name, + name: "SigningHash-ServerKeyExchange-Verify-" + alg.name, config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, 42}, - {signatureRSA, hash.id}, - {signatureRSA, 255}, + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + CipherSuites: []uint16{ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + SignatureAlgorithms: []signatureAlgorithm{ + alg.id, }, }, - flags: []string{"-expect-server-key-exchange-hash", strconv.Itoa(int(hash.id))}, + flags: []string{"-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id))}, }) } - // Test that hash resolution takes the signature type into account. + // Test that algorithm selection takes the key type into account. testCases = append(testCases, testCase{ name: "SigningHash-ClientAuth-SignatureType", config: Config{ ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureECDSA, hashSHA512}, - {signatureRSA, hashSHA384}, - {signatureECDSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP521AndSHA512, + signatureRSAPKCS1WithSHA384, + signatureECDSAWithSHA1, }, }, flags: []string{ "-cert-file", path.Join(*resourceDir, rsaCertificateFile), "-key-file", path.Join(*resourceDir, rsaKeyFile), }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384, }) testCases = append(testCases, testCase{ @@ -4589,12 +4669,13 @@ func addSigningHashTests() { name: "SigningHash-ServerKeyExchange-SignatureType", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - SignatureAndHashes: []signatureAndHash{ - {signatureECDSA, hashSHA512}, - {signatureRSA, hashSHA384}, - {signatureECDSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP521AndSHA512, + signatureRSAPKCS1WithSHA384, + signatureECDSAWithSHA1, }, }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384, }) // Test that, if the list is missing, the peer falls back to SHA-1. @@ -4602,11 +4683,11 @@ func addSigningHashTests() { name: "SigningHash-ClientAuth-Fallback", config: Config{ ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, }, Bugs: ProtocolBugs{ - NoSignatureAndHashes: true, + NoSignatureAlgorithms: true, }, }, flags: []string{ @@ -4620,11 +4701,11 @@ func addSigningHashTests() { name: "SigningHash-ServerKeyExchange-Fallback", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, }, Bugs: ProtocolBugs{ - NoSignatureAndHashes: true, + NoSignatureAlgorithms: true, }, }, }) @@ -4636,13 +4717,13 @@ func addSigningHashTests() { name: "SigningHash-ClientAuth-Enforced", config: Config{ Certificates: []Certificate{rsaCertificate}, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashMD5}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithMD5, // Advertise SHA-1 so the handshake will // proceed, but the shim's preferences will be // ignored in CertificateVerify generation, so // MD5 will be chosen. - {signatureRSA, hashSHA1}, + signatureRSAPKCS1WithSHA1, }, Bugs: ProtocolBugs{ IgnorePeerSignatureAlgorithmPreferences: true, @@ -4657,8 +4738,8 @@ func addSigningHashTests() { name: "SigningHash-ServerKeyExchange-Enforced", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashMD5}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithMD5, }, Bugs: ProtocolBugs{ IgnorePeerSignatureAlgorithmPreferences: true, @@ -4674,65 +4755,65 @@ func addSigningHashTests() { name: "Agree-Digest-Fallback", config: Config{ ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashSHA512}, - {signatureRSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA512, + signatureRSAPKCS1WithSHA1, }, }, flags: []string{ "-cert-file", path.Join(*resourceDir, rsaCertificateFile), "-key-file", path.Join(*resourceDir, rsaKeyFile), }, - digestPrefs: "SHA256", - expectedClientCertSignatureHash: hashSHA1, + digestPrefs: "SHA256", + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1, }) testCases = append(testCases, testCase{ name: "Agree-Digest-SHA256", config: Config{ ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashSHA1}, - {signatureRSA, hashSHA256}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + signatureRSAPKCS1WithSHA256, }, }, flags: []string{ "-cert-file", path.Join(*resourceDir, rsaCertificateFile), "-key-file", path.Join(*resourceDir, rsaKeyFile), }, - digestPrefs: "SHA256,SHA1", - expectedClientCertSignatureHash: hashSHA256, + digestPrefs: "SHA256,SHA1", + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256, }) testCases = append(testCases, testCase{ name: "Agree-Digest-SHA1", config: Config{ ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, }, }, flags: []string{ "-cert-file", path.Join(*resourceDir, rsaCertificateFile), "-key-file", path.Join(*resourceDir, rsaKeyFile), }, - digestPrefs: "SHA512,SHA256,SHA1", - expectedClientCertSignatureHash: hashSHA1, + digestPrefs: "SHA512,SHA256,SHA1", + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1, }) testCases = append(testCases, testCase{ name: "Agree-Digest-Default", config: Config{ ClientAuth: RequireAnyClientCert, - SignatureAndHashes: []signatureAndHash{ - {signatureRSA, hashSHA256}, - {signatureECDSA, hashSHA256}, - {signatureRSA, hashSHA1}, - {signatureECDSA, hashSHA1}, + SignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + signatureECDSAWithP256AndSHA256, + signatureRSAPKCS1WithSHA1, + signatureECDSAWithSHA1, }, }, flags: []string{ "-cert-file", path.Join(*resourceDir, rsaCertificateFile), "-key-file", path.Join(*resourceDir, rsaKeyFile), }, - expectedClientCertSignatureHash: hashSHA256, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256, }) } @@ -5439,7 +5520,7 @@ func main() { addExtendedMasterSecretTests() addRenegotiationTests() addDTLSReplayTests() - addSigningHashTests() + addSignatureAlgorithmTests() addDTLSRetransmitTests() addExportKeyingMaterialTests() addTLSUniqueTests() diff --git a/ssl/test/runner/sign.go b/ssl/test/runner/sign.go new file mode 100644 index 000000000..cf2d91dc3 --- /dev/null +++ b/ssl/test/runner/sign.go @@ -0,0 +1,219 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "crypto" + "crypto/ecdsa" + "crypto/md5" + "crypto/rsa" + "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/asn1" + "errors" + "fmt" + "math/big" +) + +type signer interface { + supportsKey(key crypto.PrivateKey) bool + signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) + verifyMessage(key crypto.PublicKey, msg, sig []byte) error +} + +func selectSignatureAlgorithm(version uint16, key crypto.PrivateKey, peerSigAlgs, ourSigAlgs []signatureAlgorithm) (signatureAlgorithm, error) { + // If the client didn't specify any signature_algorithms extension then + // we can assume that it supports SHA1. See + // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + if len(peerSigAlgs) == 0 { + peerSigAlgs = []signatureAlgorithm{signatureRSAPKCS1WithSHA1, signatureECDSAWithSHA1} + } + + for _, sigAlg := range ourSigAlgs { + if !isSupportedSignatureAlgorithm(sigAlg, peerSigAlgs) { + continue + } + + signer, err := getSigner(version, key, sigAlg) + if err != nil { + continue + } + + if signer.supportsKey(key) { + return sigAlg, nil + } + } + return 0, errors.New("tls: no common signature algorithms") +} + +func signMessage(version uint16, key crypto.PrivateKey, config *Config, sigAlg signatureAlgorithm, msg []byte) ([]byte, error) { + signer, err := getSigner(version, key, sigAlg) + if err != nil { + return nil, err + } + + return signer.signMessage(key, config, msg) +} + +func verifyMessage(version uint16, key crypto.PublicKey, sigAlg signatureAlgorithm, msg, sig []byte) error { + signer, err := getSigner(version, key, sigAlg) + if err != nil { + return err + } + + return signer.verifyMessage(key, msg, sig) +} + +type rsaPKCS1Signer struct { + hash crypto.Hash +} + +func (r *rsaPKCS1Signer) computeHash(msg []byte) []byte { + if r.hash == crypto.MD5SHA1 { + // crypto.MD5SHA1 is not a real hash function. + hashMD5 := md5.New() + hashMD5.Write(msg) + hashSHA1 := sha1.New() + hashSHA1.Write(msg) + return hashSHA1.Sum(hashMD5.Sum(nil)) + } + + h := r.hash.New() + h.Write(msg) + return h.Sum(nil) +} + +func (r *rsaPKCS1Signer) supportsKey(key crypto.PrivateKey) bool { + _, ok := key.(*rsa.PrivateKey) + return ok +} + +func (r *rsaPKCS1Signer) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) { + rsaKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("invalid key type for RSA-PKCS1") + } + + return rsa.SignPKCS1v15(config.rand(), rsaKey, r.hash, r.computeHash(msg)) +} + +func (r *rsaPKCS1Signer) verifyMessage(key crypto.PublicKey, msg, sig []byte) error { + rsaKey, ok := key.(*rsa.PublicKey) + if !ok { + return errors.New("invalid key type for RSA-PKCS1") + } + + return rsa.VerifyPKCS1v15(rsaKey, r.hash, r.computeHash(msg), sig) +} + +type ecdsaSigner struct { + // TODO(davidben): In TLS 1.3, ECDSA signatures must match curves as + // well. Pass in a curve to enforce in 1.3 alone. + hash crypto.Hash +} + +func (e *ecdsaSigner) supportsKey(key crypto.PrivateKey) bool { + _, ok := key.(*ecdsa.PrivateKey) + return ok +} + +func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int { + switch typeOfCorruption { + case BadValueNone: + return n + case BadValueNegative: + return new(big.Int).Neg(n) + case BadValueZero: + return big.NewInt(0) + case BadValueLimit: + return limit + case BadValueLarge: + bad := new(big.Int).Set(limit) + return bad.Lsh(bad, 20) + default: + panic("unknown BadValue type") + } +} + +func (e *ecdsaSigner) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) { + ecdsaKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.New("invalid key type for ECDSA") + } + + h := e.hash.New() + h.Write(msg) + digest := h.Sum(nil) + + r, s, err := ecdsa.Sign(config.rand(), ecdsaKey, digest) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } + order := ecdsaKey.Curve.Params().N + r = maybeCorruptECDSAValue(r, config.Bugs.BadECDSAR, order) + s = maybeCorruptECDSAValue(s, config.Bugs.BadECDSAS, order) + return asn1.Marshal(ecdsaSignature{r, s}) +} + +func (e *ecdsaSigner) verifyMessage(key crypto.PublicKey, msg, sig []byte) error { + ecdsaKey, ok := key.(*ecdsa.PublicKey) + if !ok { + return errors.New("invalid key type for ECDSA") + } + + ecdsaSig := new(ecdsaSignature) + if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("ECDSA signature contained zero or negative values") + } + + h := e.hash.New() + h.Write(msg) + if !ecdsa.Verify(ecdsaKey, h.Sum(nil), ecdsaSig.R, ecdsaSig.S) { + return errors.New("ECDSA verification failure") + } + return nil +} + +func getSigner(version uint16, key interface{}, sigAlg signatureAlgorithm) (signer, error) { + // TLS 1.1 and below use legacy signature algorithms. + if version < VersionTLS12 { + switch key.(type) { + case *rsa.PrivateKey, *rsa.PublicKey: + return &rsaPKCS1Signer{crypto.MD5SHA1}, nil + case *ecdsa.PrivateKey, *ecdsa.PublicKey: + return &ecdsaSigner{crypto.SHA1}, nil + default: + return nil, errors.New("unknown key type") + } + } + + // TODO(davidben): Implement RSA-PSS for TLS 1.3. + switch sigAlg { + case signatureRSAPKCS1WithMD5: + return &rsaPKCS1Signer{crypto.MD5}, nil + case signatureRSAPKCS1WithSHA1: + return &rsaPKCS1Signer{crypto.SHA1}, nil + case signatureRSAPKCS1WithSHA256: + return &rsaPKCS1Signer{crypto.SHA256}, nil + case signatureRSAPKCS1WithSHA384: + return &rsaPKCS1Signer{crypto.SHA384}, nil + case signatureRSAPKCS1WithSHA512: + return &rsaPKCS1Signer{crypto.SHA512}, nil + case signatureECDSAWithSHA1: + return &ecdsaSigner{crypto.SHA1}, nil + case signatureECDSAWithP256AndSHA256: + return &ecdsaSigner{crypto.SHA256}, nil + case signatureECDSAWithP384AndSHA384: + return &ecdsaSigner{crypto.SHA384}, nil + case signatureECDSAWithP521AndSHA512: + return &ecdsaSigner{crypto.SHA512}, nil + default: + return nil, fmt.Errorf("unsupported signature algorithm %04x", sigAlg) + } +} diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index cf3541db8..32b91e9e5 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc @@ -145,8 +145,8 @@ const Flag kIntFlags[] = { { "-mtu", &TestConfig::mtu }, { "-export-keying-material", &TestConfig::export_keying_material }, { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations }, - { "-expect-server-key-exchange-hash", - &TestConfig::expect_server_key_exchange_hash }, + { "-expect-peer-signature-algorithm", + &TestConfig::expect_peer_signature_algorithm }, { "-expect-curve-id", &TestConfig::expect_curve_id }, { "-expect-dhe-group-size", &TestConfig::expect_dhe_group_size }, { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms }, diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index b554f14ea..a3e9ad157 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h @@ -99,7 +99,7 @@ struct TestConfig { bool renegotiate_freely = false; bool renegotiate_ignore = false; bool disable_npn = false; - int expect_server_key_exchange_hash = 0; + int expect_peer_signature_algorithm = 0; bool p384_only = false; bool enable_all_curves = false; bool use_sparse_dh_prime = false; From 4c3ddf7ec0b653e959bcdac8f3ce882501b9cc54 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 29 Jun 2016 18:13:53 -0400 Subject: [PATCH 15/40] Explicitly mark nearly every test at TLS 1.2. In preparation for TLS 1.3 using its actual handshake, switch most tests to TLS 1.3 and add liberal TODOs for the tests which will need TLS 1.3 variants. In doing so, move a few tests from basic tests into one of the groups. Also rename BadECDSACurve to BadECDHECurve (it was never ECDSA) and add a test to make sure FALLBACK_SCSV is correctly sensitive to the maximum version. Change-Id: Ifca6cf8f7a48d6f069483c0aab192ae691b1dd8e Reviewed-on: https://boringssl-review.googlesource.com/8560 Reviewed-by: David Benjamin --- ssl/test/runner/common.go | 6 +- ssl/test/runner/key_agreement.go | 6 +- ssl/test/runner/runner.go | 668 ++++++++++++++++++++++--------- 3 files changed, 483 insertions(+), 197 deletions(-) diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 56a7c4982..1012810fd 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -429,9 +429,9 @@ type ProtocolBugs struct { // CertificateVerify message should be invalid. InvalidCertVerifySignature bool - // InvalidSKXCurve causes the curve ID in the ServerKeyExchange message - // to be wrong. - InvalidSKXCurve bool + // SendCurve, if non-zero, causes the ServerKeyExchange message to use + // the specified curve ID rather than the negotiated one. + SendCurve CurveID // InvalidECDHPoint, if true, causes the ECC points in // ServerKeyExchange or ClientKeyExchange messages to be invalid. diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go index cb02c53a1..5f8e7ea61 100644 --- a/ssl/test/runner/key_agreement.go +++ b/ssl/test/runner/key_agreement.go @@ -529,11 +529,11 @@ NextCandidate: // http://tools.ietf.org/html/rfc4492#section-5.4 serverECDHParams := make([]byte, 1+2+1+len(publicKey)) serverECDHParams[0] = 3 // named curve + if config.Bugs.SendCurve != 0 { + curveid = config.Bugs.SendCurve + } serverECDHParams[1] = byte(curveid >> 8) serverECDHParams[2] = byte(curveid) - if config.Bugs.InvalidSKXCurve { - serverECDHParams[2] ^= 0xff - } serverECDHParams[3] = byte(len(publicKey)) copy(serverECDHParams[4:], publicKey) if config.Bugs.InvalidECDHPoint { diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 41b886cb3..be88af6e3 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -1006,6 +1006,8 @@ func addBasicTests() { { name: "BadRSASignature", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Bugs: ProtocolBugs{ InvalidSKXSignature: true, @@ -1017,6 +1019,8 @@ func addBasicTests() { { name: "BadECDSASignature", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, Bugs: ProtocolBugs{ InvalidSKXSignature: true, @@ -1030,6 +1034,8 @@ func addBasicTests() { testType: serverTest, name: "BadRSASignature-ClientAuth", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ InvalidCertVerifySignature: true, }, @@ -1043,6 +1049,8 @@ func addBasicTests() { testType: serverTest, name: "BadECDSASignature-ClientAuth", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ InvalidCertVerifySignature: true, }, @@ -1052,18 +1060,6 @@ func addBasicTests() { expectedError: ":BAD_SIGNATURE:", flags: []string{"-require-any-client-certificate"}, }, - { - name: "BadECDSACurve", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - InvalidSKXCurve: true, - }, - Certificates: []Certificate{getECDSACertificate()}, - }, - shouldFail: true, - expectedError: ":WRONG_CURVE:", - }, { name: "NoFallbackSCSV", config: Config{ @@ -1086,6 +1082,7 @@ func addBasicTests() { { name: "ClientCertificateTypes", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequestClientCert, ClientCertificateTypes: []byte{ CertTypeDSSSign, @@ -1102,17 +1099,10 @@ func addBasicTests() { }), }, }, - { - name: "NoClientCertificate", - config: Config{ - ClientAuth: RequireAnyClientCert, - }, - shouldFail: true, - expectedLocalError: "client didn't provide a certificate", - }, { name: "UnauthenticatedECDH", config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Bugs: ProtocolBugs{ UnauthenticatedECDH: true, @@ -1124,6 +1114,7 @@ func addBasicTests() { { name: "SkipCertificateStatus", config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Bugs: ProtocolBugs{ SkipCertificateStatus: true, @@ -1136,6 +1127,7 @@ func addBasicTests() { { name: "SkipServerKeyExchange", config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Bugs: ProtocolBugs{ SkipServerKeyExchange: true, @@ -1147,6 +1139,7 @@ func addBasicTests() { { name: "SkipChangeCipherSpec-Client", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SkipChangeCipherSpec: true, }, @@ -1158,6 +1151,7 @@ func addBasicTests() { testType: serverTest, name: "SkipChangeCipherSpec-Server", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SkipChangeCipherSpec: true, }, @@ -1169,6 +1163,7 @@ func addBasicTests() { testType: serverTest, name: "SkipChangeCipherSpec-Server-NPN", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"bar"}, Bugs: ProtocolBugs{ SkipChangeCipherSpec: true, @@ -1183,6 +1178,7 @@ func addBasicTests() { { name: "FragmentAcrossChangeCipherSpec-Client", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ FragmentAcrossChangeCipherSpec: true, }, @@ -1194,6 +1190,7 @@ func addBasicTests() { testType: serverTest, name: "FragmentAcrossChangeCipherSpec-Server", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ FragmentAcrossChangeCipherSpec: true, }, @@ -1205,6 +1202,7 @@ func addBasicTests() { testType: serverTest, name: "FragmentAcrossChangeCipherSpec-Server-NPN", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"bar"}, Bugs: ProtocolBugs{ FragmentAcrossChangeCipherSpec: true, @@ -1293,6 +1291,7 @@ func addBasicTests() { testType: serverTest, name: "EarlyChangeCipherSpec-server-1", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ EarlyChangeCipherSpec: 1, }, @@ -1304,6 +1303,7 @@ func addBasicTests() { testType: serverTest, name: "EarlyChangeCipherSpec-server-2", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ EarlyChangeCipherSpec: 2, }, @@ -1315,6 +1315,10 @@ func addBasicTests() { protocol: dtls, name: "StrayChangeCipherSpec", config: Config{ + // TODO(davidben): Once DTLS 1.3 exists, test + // that stray ChangeCipherSpec messages are + // rejected. + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ StrayChangeCipherSpec: true, }, @@ -1323,6 +1327,7 @@ func addBasicTests() { { name: "SkipNewSessionTicket", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SkipNewSessionTicket: true, }, @@ -1351,6 +1356,17 @@ func addBasicTests() { }, }, }, + { + testType: serverTest, + name: "FallbackSCSV-VersionMatch-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, + }, { testType: serverTest, name: "FragmentedClientVersion", @@ -1460,14 +1476,6 @@ func addBasicTests() { shouldFail: true, expectedError: ":WRONG_SSL_VERSION:", }, - { - name: "NoSharedCipher", - config: Config{ - CipherSuites: []uint16{}, - }, - shouldFail: true, - expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", - }, { protocol: dtls, testType: serverTest, @@ -1495,6 +1503,8 @@ func addBasicTests() { { name: "CertMismatchRSA", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this test. + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, Certificates: []Certificate{getECDSACertificate()}, Bugs: ProtocolBugs{ @@ -1507,6 +1517,8 @@ func addBasicTests() { { name: "CertMismatchECDSA", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this test. + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Certificates: []Certificate{getRSACertificate()}, Bugs: ProtocolBugs{ @@ -1519,6 +1531,8 @@ func addBasicTests() { { name: "EmptyCertificateList", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this test. + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Bugs: ProtocolBugs{ EmptyCertificateList: true, @@ -1589,6 +1603,7 @@ func addBasicTests() { { name: "AppDataAfterChangeCipherSpec", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), }, @@ -1599,6 +1614,7 @@ func addBasicTests() { { name: "AppDataAfterChangeCipherSpec-Empty", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ AppDataAfterChangeCipherSpec: []byte{}, }, @@ -1610,6 +1626,7 @@ func addBasicTests() { protocol: dtls, name: "AppDataAfterChangeCipherSpec-DTLS", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), }, @@ -1621,6 +1638,7 @@ func addBasicTests() { protocol: dtls, name: "AppDataAfterChangeCipherSpec-DTLS-Empty", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ AppDataAfterChangeCipherSpec: []byte{}, }, @@ -1631,6 +1649,7 @@ func addBasicTests() { { name: "AlertAfterChangeCipherSpec", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ AlertAfterChangeCipherSpec: alertRecordOverflow, }, @@ -1642,6 +1661,7 @@ func addBasicTests() { protocol: dtls, name: "AlertAfterChangeCipherSpec-DTLS", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ AlertAfterChangeCipherSpec: alertRecordOverflow, }, @@ -1845,34 +1865,11 @@ func addBasicTests() { }, }, }, - { - name: "UnsupportedCipherSuite", - config: Config{ - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, - Bugs: ProtocolBugs{ - IgnorePeerCipherPreferences: true, - }, - }, - flags: []string{"-cipher", "DEFAULT:!RC4"}, - shouldFail: true, - expectedError: ":WRONG_CIPHER_RETURNED:", - }, - { - name: "UnsupportedCurve", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - CurvePreferences: []CurveID{CurveP256}, - Bugs: ProtocolBugs{ - IgnorePeerCurvePreferences: true, - }, - }, - flags: []string{"-p384-only"}, - shouldFail: true, - expectedError: ":WRONG_CURVE:", - }, { name: "BadFinished-Client", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadFinished: true, }, @@ -1884,6 +1881,8 @@ func addBasicTests() { testType: serverTest, name: "BadFinished-Server", config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadFinished: true, }, @@ -1989,31 +1988,6 @@ func addBasicTests() { expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", expectedLocalError: "tls: peer did not false start: EOF", }, - { - testType: serverTest, - name: "NoSupportedCurves", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - Bugs: ProtocolBugs{ - NoSupportedCurves: true, - }, - }, - shouldFail: true, - expectedError: ":NO_SHARED_CIPHER:", - }, - { - testType: serverTest, - name: "NoCommonCurves", - config: Config{ - MaxVersion: VersionTLS12, - CipherSuites: []uint16{ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - }, - CurvePreferences: []CurveID{CurveP224}, - }, - expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, - }, { protocol: dtls, name: "SendSplitAlert-Sync", @@ -2086,6 +2060,7 @@ func addBasicTests() { { name: "EmptySessionID", config: Config{ + MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, noSessionCache: true, @@ -2175,8 +2150,11 @@ func addBasicTests() { expectMessageDropped: true, }, { + // In TLS 1.2 and below, empty NewSessionTicket messages + // mean the server changed its mind on sending a ticket. name: "SendEmptySessionTicket", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendEmptySessionTicket: true, FailIfSessionOffered: true, @@ -2186,19 +2164,10 @@ func addBasicTests() { resumeSession: true, expectResumeRejected: true, }, - { - name: "CheckLeafCurve", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - Certificates: []Certificate{getECDSACertificate()}, - }, - flags: []string{"-p384-only"}, - shouldFail: true, - expectedError: ":BAD_ECC_CERT:", - }, { name: "BadChangeCipherSpec-1", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadChangeCipherSpec: []byte{2}, }, @@ -2209,6 +2178,7 @@ func addBasicTests() { { name: "BadChangeCipherSpec-2", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadChangeCipherSpec: []byte{1, 1}, }, @@ -2220,6 +2190,7 @@ func addBasicTests() { protocol: dtls, name: "BadChangeCipherSpec-DTLS-1", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadChangeCipherSpec: []byte{2}, }, @@ -2231,6 +2202,7 @@ func addBasicTests() { protocol: dtls, name: "BadChangeCipherSpec-DTLS-2", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadChangeCipherSpec: []byte{1, 1}, }, @@ -2242,6 +2214,7 @@ func addBasicTests() { name: "BadHelloRequest-1", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadHelloRequest: []byte{typeHelloRequest, 0, 0, 1, 1}, }, @@ -2257,6 +2230,7 @@ func addBasicTests() { name: "BadHelloRequest-2", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadHelloRequest: []byte{typeServerKeyExchange, 0, 0, 0}, }, @@ -2272,36 +2246,14 @@ func addBasicTests() { testType: serverTest, name: "SupportTicketsWithSessionID", config: Config{ + MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, - resumeConfig: &Config{}, + resumeConfig: &Config{ + MaxVersion: VersionTLS12, + }, resumeSession: true, }, - { - name: "InvalidECDHPoint-Client", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - CurvePreferences: []CurveID{CurveP256}, - Bugs: ProtocolBugs{ - InvalidECDHPoint: true, - }, - }, - shouldFail: true, - expectedError: ":INVALID_ENCODING:", - }, - { - testType: serverTest, - name: "InvalidECDHPoint-Server", - config: Config{ - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - CurvePreferences: []CurveID{CurveP256}, - Bugs: ProtocolBugs{ - InvalidECDHPoint: true, - }, - }, - shouldFail: true, - expectedError: ":INVALID_ENCODING:", - }, } testCases = append(testCases, basicTests...) } @@ -2444,6 +2396,31 @@ func addCipherSuiteTests() { } } + testCases = append(testCases, testCase{ + name: "NoSharedCipher", + config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this test. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{}, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", + }) + + testCases = append(testCases, testCase{ + name: "UnsupportedCipherSuite", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + IgnorePeerCipherPreferences: true, + }, + }, + flags: []string{"-cipher", "DEFAULT:!RC4"}, + shouldFail: true, + expectedError: ":WRONG_CIPHER_RETURNED:", + }) + testCases = append(testCases, testCase{ name: "WeakDH", config: Config{ @@ -2752,6 +2729,16 @@ func addClientAuthTests() { // TODO(davidben): These tests will need TLS 1.3 versions when the // handshake is separate. + testCases = append(testCases, testCase{ + name: "NoClientCertificate", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + }, + shouldFail: true, + expectedLocalError: "client didn't provide a certificate", + }) + testCases = append(testCases, testCase{ testType: serverTest, name: "RequireAnyClientCertificate", @@ -2845,6 +2832,9 @@ func addExtendedMasterSecretTests() { testType = clientTest } + // TODO(davidben): Once the new TLS 1.3 handshake is in, + // test that the extension is irrelevant, but the API + // acts as if it is enabled. for _, ver := range tlsVersions { test := testCase{ testType: testType, @@ -2885,12 +2875,14 @@ func addExtendedMasterSecretTests() { } supportedConfig := Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ RequireExtendedMasterSecret: true, }, } noSupportConfig := Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoExtendedMasterSecret: true, }, @@ -2963,13 +2955,16 @@ func addExtendedMasterSecretTests() { func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) { var tests []testCase - // TODO(davidben): These tests will need both TLS 1.2 and TLS 1.3 - // versions when the handshake becomes completely different. - // Basic handshake, with resumption. Client and server, // session ID and session ticket. + // + // TODO(davidben): Add TLS 1.3 tests for all of its different handshake + // shapes. tests = append(tests, testCase{ - name: "Basic-Client", + name: "Basic-Client", + config: Config{ + MaxVersion: VersionTLS12, + }, resumeSession: true, // Ensure session tickets are used, not session IDs. noSessionCache: true, @@ -2977,6 +2972,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) tests = append(tests, testCase{ name: "Basic-Client-RenewTicket", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ RenewTicketOnResume: true, }, @@ -2987,12 +2983,16 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) tests = append(tests, testCase{ name: "Basic-Client-NoTicket", config: Config{ + MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, resumeSession: true, }) tests = append(tests, testCase{ - name: "Basic-Client-Implicit", + name: "Basic-Client-Implicit", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{"-implicit-handshake"}, resumeSession: true, }) @@ -3000,6 +3000,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: serverTest, name: "Basic-Server", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ RequireSessionTickets: true, }, @@ -3010,34 +3011,47 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: serverTest, name: "Basic-Server-NoTickets", config: Config{ + MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, resumeSession: true, }) tests = append(tests, testCase{ - testType: serverTest, - name: "Basic-Server-Implicit", + testType: serverTest, + name: "Basic-Server-Implicit", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{"-implicit-handshake"}, resumeSession: true, }) tests = append(tests, testCase{ - testType: serverTest, - name: "Basic-Server-EarlyCallback", + testType: serverTest, + name: "Basic-Server-EarlyCallback", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{"-use-early-callback"}, resumeSession: true, }) // TLS client auth. + // + // TODO(davidben): Add TLS 1.3 client auth tests. tests = append(tests, testCase{ testType: clientTest, name: "ClientAuth-NoCertificate-Client", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequestClientCert, }, }) tests = append(tests, testCase{ testType: serverTest, name: "ClientAuth-NoCertificate-Server", + config: Config{ + MaxVersion: VersionTLS12, + }, // Setting SSL_VERIFY_PEER allows anonymous clients. flags: []string{"-verify-peer"}, }) @@ -3060,18 +3074,11 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) flags: []string{"-verify-peer"}, }) } - tests = append(tests, testCase{ - testType: clientTest, - name: "ClientAuth-NoCertificate-OldCallback", - config: Config{ - ClientAuth: RequestClientCert, - }, - flags: []string{"-use-old-client-cert-callback"}, - }) tests = append(tests, testCase{ testType: clientTest, name: "ClientAuth-RSA-Client", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, }, flags: []string{ @@ -3083,6 +3090,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: clientTest, name: "ClientAuth-ECDSA-Client", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, }, flags: []string{ @@ -3090,10 +3098,20 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) "-key-file", path.Join(*resourceDir, ecdsaKeyFile), }, }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-NoCertificate-OldCallback", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequestClientCert, + }, + flags: []string{"-use-old-client-cert-callback"}, + }) tests = append(tests, testCase{ testType: clientTest, name: "ClientAuth-OldCallback", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, }, flags: []string{ @@ -3102,59 +3120,61 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) "-use-old-client-cert-callback", }, }) - - if async { - // Test async keys against each key exchange. - tests = append(tests, testCase{ - testType: serverTest, - name: "Basic-Server-RSA", - config: Config{ - MaxVersion: VersionTLS12, - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, - }, - flags: []string{ - "-cert-file", path.Join(*resourceDir, rsaCertificateFile), - "-key-file", path.Join(*resourceDir, rsaKeyFile), - }, - }) - tests = append(tests, testCase{ - testType: serverTest, - name: "Basic-Server-ECDHE-RSA", - config: Config{ - MaxVersion: VersionTLS12, - CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - }, - flags: []string{ - "-cert-file", path.Join(*resourceDir, rsaCertificateFile), - "-key-file", path.Join(*resourceDir, rsaKeyFile), - }, - }) - tests = append(tests, testCase{ - testType: serverTest, - name: "Basic-Server-ECDHE-ECDSA", - config: Config{ - MaxVersion: VersionTLS12, - CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - }, - flags: []string{ - "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile), - "-key-file", path.Join(*resourceDir, ecdsaKeyFile), - }, - }) - } tests = append(tests, testCase{ testType: serverTest, name: "ClientAuth-Server", config: Config{ + MaxVersion: VersionTLS12, Certificates: []Certificate{rsaCertificate}, }, flags: []string{"-require-any-client-certificate"}, }) + // Test each key exchange on the server side for async keys. + // + // TODO(davidben): Add TLS 1.3 versions of these. + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-RSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-ECDHE-RSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-ECDHE-ECDSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaCertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaKeyFile), + }, + }) + // No session ticket support; server doesn't send NewSessionTicket. tests = append(tests, testCase{ name: "SessionTicketsDisabled-Client", config: Config{ + MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, }) @@ -3162,6 +3182,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: serverTest, name: "SessionTicketsDisabled-Server", config: Config{ + MaxVersion: VersionTLS12, SessionTicketsDisabled: true, }, }) @@ -3188,9 +3209,15 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) flags: []string{"-psk", "secret"}, }) + // OCSP stapling tests. + // + // TODO(davidben): Test the TLS 1.3 version of OCSP stapling. tests = append(tests, testCase{ testType: clientTest, name: "OCSPStapling-Client", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{ "-enable-ocsp-stapling", "-expect-ocsp-response", @@ -3199,10 +3226,12 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) }, resumeSession: true, }) - tests = append(tests, testCase{ - testType: serverTest, - name: "OCSPStapling-Server", + testType: serverTest, + name: "OCSPStapling-Server", + config: Config{ + MaxVersion: VersionTLS12, + }, expectedOCSPResponse: testOCSPResponse, flags: []string{ "-ocsp-response", @@ -3211,17 +3240,25 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) resumeSession: true, }) + // Certificate verification tests. + // + // TODO(davidben): Test the TLS 1.3 version. tests = append(tests, testCase{ testType: clientTest, name: "CertificateVerificationSucceed", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{ "-verify-peer", }, }) - tests = append(tests, testCase{ testType: clientTest, name: "CertificateVerificationFail", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{ "-verify-fail", "-verify-peer", @@ -3229,10 +3266,12 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) shouldFail: true, expectedError: ":CERTIFICATE_VERIFY_FAILED:", }) - tests = append(tests, testCase{ testType: clientTest, name: "CertificateVerificationSoftFail", + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{ "-verify-fail", "-expect-verify-result", @@ -3241,17 +3280,22 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) if protocol == tls { tests = append(tests, testCase{ - name: "Renegotiate-Client", + name: "Renegotiate-Client", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 1, flags: []string{ "-renegotiate-freely", "-expect-total-renegotiations", "1", }, }) + // NPN on client and server; results in post-handshake message. tests = append(tests, testCase{ name: "NPN-Client", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo"}, }, flags: []string{"-select-next-proto", "foo"}, @@ -3263,6 +3307,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: serverTest, name: "NPN-Server", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"bar"}, }, flags: []string{ @@ -3369,6 +3414,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) tests = append(tests, testCase{ name: "ChannelID-Client", config: Config{ + MaxVersion: VersionTLS12, RequestChannelID: true, }, flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)}, @@ -3381,7 +3427,8 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: serverTest, name: "ChannelID-Server", config: Config{ - ChannelID: channelIDKey, + MaxVersion: VersionTLS12, + ChannelID: channelIDKey, }, flags: []string{ "-expect-channel-id", @@ -3396,6 +3443,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) tests = append(tests, testCase{ name: "ChannelID-NPN-Client", config: Config{ + MaxVersion: VersionTLS12, RequestChannelID: true, NextProtos: []string{"foo"}, }, @@ -3412,6 +3460,7 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) testType: serverTest, name: "ChannelID-NPN-Server", config: Config{ + MaxVersion: VersionTLS12, ChannelID: channelIDKey, NextProtos: []string{"bar"}, }, @@ -3454,9 +3503,12 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) flags: []string{"-check-close-notify"}, }) } else { + // TODO(davidben): DTLS 1.3 will want a similar thing for + // HelloRetryRequest. tests = append(tests, testCase{ name: "SkipHelloVerifyRequest", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SkipHelloVerifyRequest: true, }, @@ -3496,9 +3548,14 @@ func addDDoSCallbackTests() { suffix = "No" + suffix } + // TODO(davidben): Test TLS 1.3's version of the DDoS callback. + testCases = append(testCases, testCase{ - testType: serverTest, - name: "Server-DDoS-OK-" + suffix, + testType: serverTest, + name: "Server-DDoS-OK-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{"-install-ddos-callback"}, resumeSession: resume, }) @@ -3508,8 +3565,11 @@ func addDDoSCallbackTests() { failFlag = "-fail-second-ddos-callback" } testCases = append(testCases, testCase{ - testType: serverTest, - name: "Server-DDoS-Reject-" + suffix, + testType: serverTest, + name: "Server-DDoS-Reject-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{"-install-ddos-callback", failFlag}, resumeSession: resume, shouldFail: true, @@ -3711,10 +3771,15 @@ func addMinimumVersionTests() { } func addExtensionTests() { + // TODO(davidben): Extensions, where applicable, all move their server + // halves to EncryptedExtensions in TLS 1.3. Duplicate each of these + // tests for both. Also test interaction with 0-RTT when implemented. + testCases = append(testCases, testCase{ testType: clientTest, name: "DuplicateExtensionClient", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ DuplicateExtension: true, }, @@ -3726,6 +3791,7 @@ func addExtensionTests() { testType: serverTest, name: "DuplicateExtensionServer", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ DuplicateExtension: true, }, @@ -3737,6 +3803,7 @@ func addExtensionTests() { testType: clientTest, name: "ServerNameExtensionClient", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ ExpectServerName: "example.com", }, @@ -3747,6 +3814,7 @@ func addExtensionTests() { testType: clientTest, name: "ServerNameExtensionClientMismatch", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ ExpectServerName: "mismatch.com", }, @@ -3759,6 +3827,7 @@ func addExtensionTests() { testType: clientTest, name: "ServerNameExtensionClientMissing", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ ExpectServerName: "missing.com", }, @@ -3770,6 +3839,7 @@ func addExtensionTests() { testType: serverTest, name: "ServerNameExtensionServer", config: Config{ + MaxVersion: VersionTLS12, ServerName: "example.com", }, flags: []string{"-expect-server-name", "example.com"}, @@ -3779,6 +3849,7 @@ func addExtensionTests() { testType: clientTest, name: "ALPNClient", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo"}, }, flags: []string{ @@ -3793,6 +3864,7 @@ func addExtensionTests() { testType: serverTest, name: "ALPNServer", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo", "bar", "baz"}, }, flags: []string{ @@ -3807,6 +3879,7 @@ func addExtensionTests() { testType: serverTest, name: "ALPNServer-Decline", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo", "bar", "baz"}, }, flags: []string{"-decline-alpn"}, @@ -3818,6 +3891,7 @@ func addExtensionTests() { testType: serverTest, name: "ALPNServer-Preferred", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo", "bar", "baz"}, }, flags: []string{ @@ -3833,6 +3907,7 @@ func addExtensionTests() { testType: serverTest, name: "ALPNServer-Preferred-Swapped", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo", "bar", "baz"}, Bugs: ProtocolBugs{ SwapNPNAndALPN: true, @@ -3852,6 +3927,7 @@ func addExtensionTests() { testType: clientTest, name: "ALPNClient-EmptyProtocolName", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{""}, Bugs: ProtocolBugs{ // A server returning an empty ALPN protocol @@ -3869,6 +3945,7 @@ func addExtensionTests() { testType: serverTest, name: "ALPNServer-EmptyProtocolName", config: Config{ + MaxVersion: VersionTLS12, // A ClientHello containing an empty ALPN protocol // should be rejected. NextProtos: []string{"foo", "", "baz"}, @@ -3883,6 +3960,7 @@ func addExtensionTests() { testCases = append(testCases, testCase{ name: "NegotiateALPNAndNPN", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo", "bar", "baz"}, Bugs: ProtocolBugs{ NegotiateALPNAndNPN: true, @@ -3898,6 +3976,7 @@ func addExtensionTests() { testCases = append(testCases, testCase{ name: "NegotiateALPNAndNPN-Swapped", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo", "bar", "baz"}, Bugs: ProtocolBugs{ NegotiateALPNAndNPN: true, @@ -3915,6 +3994,7 @@ func addExtensionTests() { testCases = append(testCases, testCase{ name: "DisableNPN", config: Config{ + MaxVersion: VersionTLS12, NextProtos: []string{"foo"}, }, flags: []string{ @@ -3928,6 +4008,7 @@ func addExtensionTests() { testType: serverTest, name: "CorruptTicket", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ CorruptTicket: true, }, @@ -3937,8 +4018,11 @@ func addExtensionTests() { }) // Test the ticket callback, with and without renewal. testCases = append(testCases, testCase{ - testType: serverTest, - name: "TicketCallback", + testType: serverTest, + name: "TicketCallback", + config: Config{ + MaxVersion: VersionTLS12, + }, resumeSession: true, flags: []string{"-use-ticket-callback"}, }) @@ -3946,6 +4030,7 @@ func addExtensionTests() { testType: serverTest, name: "TicketCallback-Renew", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ ExpectNewTicket: true, }, @@ -3958,6 +4043,7 @@ func addExtensionTests() { testType: serverTest, name: "OversizedSessionId", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ OversizedSessionId: true, }, @@ -3972,6 +4058,7 @@ func addExtensionTests() { protocol: dtls, name: "SRTP-Client", config: Config{ + MaxVersion: VersionTLS12, SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42}, }, flags: []string{ @@ -3985,6 +4072,7 @@ func addExtensionTests() { testType: serverTest, name: "SRTP-Server", config: Config{ + MaxVersion: VersionTLS12, SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42}, }, flags: []string{ @@ -3999,6 +4087,7 @@ func addExtensionTests() { testType: serverTest, name: "SRTP-Server-IgnoreMKI", config: Config{ + MaxVersion: VersionTLS12, SRTPProtectionProfiles: []uint16{SRTP_AES128_CM_HMAC_SHA1_80}, Bugs: ProtocolBugs{ SRTPMasterKeyIdentifer: "bogus", @@ -4017,6 +4106,7 @@ func addExtensionTests() { testType: serverTest, name: "SRTP-Server-NoMatch", config: Config{ + MaxVersion: VersionTLS12, SRTPProtectionProfiles: []uint16{100, 101, 102}, }, flags: []string{ @@ -4031,6 +4121,7 @@ func addExtensionTests() { protocol: dtls, name: "SRTP-Client-NoMatch", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_32, }, @@ -4046,6 +4137,9 @@ func addExtensionTests() { testCases = append(testCases, testCase{ name: "SignedCertificateTimestampList-Client", testType: clientTest, + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{ "-enable-signed-cert-timestamps", "-expect-signed-cert-timestamps", @@ -4056,6 +4150,7 @@ func addExtensionTests() { testCases = append(testCases, testCase{ name: "SendSCTListOnResume", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendSCTListOnResume: []byte("bogus"), }, @@ -4070,6 +4165,9 @@ func addExtensionTests() { testCases = append(testCases, testCase{ name: "SignedCertificateTimestampList-Server", testType: serverTest, + config: Config{ + MaxVersion: VersionTLS12, + }, flags: []string{ "-signed-cert-timestamps", base64.StdEncoding.EncodeToString(testSCTList), @@ -4077,6 +4175,7 @@ func addExtensionTests() { expectedSCTList: testSCTList, resumeSession: true, }) + testCases = append(testCases, testCase{ testType: clientTest, name: "ClientHelloPadding", @@ -4275,8 +4374,11 @@ func addResumptionVersionTests() { func addRenegotiationTests() { // Servers cannot renegotiate. testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-Forbidden", + testType: serverTest, + name: "Renegotiate-Server-Forbidden", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 1, shouldFail: true, expectedError: ":NO_RENEGOTIATION:", @@ -4288,6 +4390,7 @@ func addRenegotiationTests() { testType: serverTest, name: "Renegotiate-Server-NoExt", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoRenegotiationInfo: true, RequireRenegotiationInfo: true, @@ -4302,6 +4405,7 @@ func addRenegotiationTests() { testType: serverTest, name: "Renegotiate-Server-NoExt-SCSV", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoRenegotiationInfo: true, SendRenegotiationSCSV: true, @@ -4312,6 +4416,7 @@ func addRenegotiationTests() { testCases = append(testCases, testCase{ name: "Renegotiate-Client", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ FailIfResumeOnRenego: true, }, @@ -4326,6 +4431,7 @@ func addRenegotiationTests() { name: "Renegotiate-Client-EmptyExt", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ EmptyRenegotiationInfo: true, }, @@ -4338,6 +4444,7 @@ func addRenegotiationTests() { name: "Renegotiate-Client-BadExt", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ BadRenegotiationInfo: true, }, @@ -4350,6 +4457,7 @@ func addRenegotiationTests() { name: "Renegotiate-Client-Downgrade", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoRenegotiationInfoAfterInitial: true, }, @@ -4362,6 +4470,7 @@ func addRenegotiationTests() { name: "Renegotiate-Client-Upgrade", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoRenegotiationInfoInInitial: true, }, @@ -4374,6 +4483,7 @@ func addRenegotiationTests() { name: "Renegotiate-Client-NoExt-Allowed", renegotiate: 1, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoRenegotiationInfo: true, }, @@ -4442,14 +4552,20 @@ func addRenegotiationTests() { // Client-side renegotiation controls. testCases = append(testCases, testCase{ - name: "Renegotiate-Client-Forbidden-1", + name: "Renegotiate-Client-Forbidden-1", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 1, shouldFail: true, expectedError: ":NO_RENEGOTIATION:", expectedLocalError: "remote error: no renegotiation", }) testCases = append(testCases, testCase{ - name: "Renegotiate-Client-Once-1", + name: "Renegotiate-Client-Once-1", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 1, flags: []string{ "-renegotiate-once", @@ -4457,7 +4573,10 @@ func addRenegotiationTests() { }, }) testCases = append(testCases, testCase{ - name: "Renegotiate-Client-Freely-1", + name: "Renegotiate-Client-Freely-1", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 1, flags: []string{ "-renegotiate-freely", @@ -4465,7 +4584,10 @@ func addRenegotiationTests() { }, }) testCases = append(testCases, testCase{ - name: "Renegotiate-Client-Once-2", + name: "Renegotiate-Client-Once-2", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 2, flags: []string{"-renegotiate-once"}, shouldFail: true, @@ -4473,7 +4595,10 @@ func addRenegotiationTests() { expectedLocalError: "remote error: no renegotiation", }) testCases = append(testCases, testCase{ - name: "Renegotiate-Client-Freely-2", + name: "Renegotiate-Client-Freely-2", + config: Config{ + MaxVersion: VersionTLS12, + }, renegotiate: 2, flags: []string{ "-renegotiate-freely", @@ -4483,6 +4608,7 @@ func addRenegotiationTests() { testCases = append(testCases, testCase{ name: "Renegotiate-Client-NoIgnore", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendHelloRequestBeforeEveryAppDataRecord: true, }, @@ -4493,6 +4619,7 @@ func addRenegotiationTests() { testCases = append(testCases, testCase{ name: "Renegotiate-Client-Ignore", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ SendHelloRequestBeforeEveryAppDataRecord: true, }, @@ -4502,6 +4629,8 @@ func addRenegotiationTests() { "-expect-total-renegotiations", "0", }, }) + + // TODO(davidben): Add a test that HelloRequests are illegal in TLS 1.3. } func addDTLSReplayTests() { @@ -4569,10 +4698,13 @@ const fakeSigAlg2 signatureAlgorithm = 0xff01 func addSignatureAlgorithmTests() { // Make sure each signature algorithm works. Include some fake values in // the list and ensure they're ignored. + // + // TODO(davidben): Test each of these against both TLS 1.2 and TLS 1.3. for _, alg := range testSignatureAlgorithms { testCases = append(testCases, testCase{ name: "SigningHash-ClientAuth-Sign-" + alg.name, config: Config{ + MaxVersion: VersionTLS12, // SignatureAlgorithms is shared, so we must // configure a matching server certificate too. Certificates: []Certificate{getRunnerCertificate(alg.cert)}, @@ -4594,6 +4726,7 @@ func addSignatureAlgorithmTests() { testType: serverTest, name: "SigningHash-ClientAuth-Verify-" + alg.name, config: Config{ + MaxVersion: VersionTLS12, Certificates: []Certificate{getRunnerCertificate(alg.cert)}, SignatureAlgorithms: []signatureAlgorithm{ alg.id, @@ -4613,6 +4746,7 @@ func addSignatureAlgorithmTests() { testType: serverTest, name: "SigningHash-ServerKeyExchange-Sign-" + alg.name, config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, @@ -4633,6 +4767,7 @@ func addSignatureAlgorithmTests() { testCases = append(testCases, testCase{ name: "SigningHash-ServerKeyExchange-Verify-" + alg.name, config: Config{ + MaxVersion: VersionTLS12, Certificates: []Certificate{getRunnerCertificate(alg.cert)}, CipherSuites: []uint16{ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, @@ -4647,10 +4782,13 @@ func addSignatureAlgorithmTests() { } // Test that algorithm selection takes the key type into account. + // + // TODO(davidben): Test this in TLS 1.3. testCases = append(testCases, testCase{ name: "SigningHash-ClientAuth-SignatureType", config: Config{ ClientAuth: RequireAnyClientCert, + MaxVersion: VersionTLS12, SignatureAlgorithms: []signatureAlgorithm{ signatureECDSAWithP521AndSHA512, signatureRSAPKCS1WithSHA384, @@ -4668,6 +4806,7 @@ func addSignatureAlgorithmTests() { testType: serverTest, name: "SigningHash-ServerKeyExchange-SignatureType", config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, SignatureAlgorithms: []signatureAlgorithm{ signatureECDSAWithP521AndSHA512, @@ -4679,9 +4818,12 @@ func addSignatureAlgorithmTests() { }) // Test that, if the list is missing, the peer falls back to SHA-1. + // + // TODO(davidben): Test this does not happen in TLS 1.3. testCases = append(testCases, testCase{ name: "SigningHash-ClientAuth-Fallback", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithSHA1, @@ -4700,6 +4842,7 @@ func addSignatureAlgorithmTests() { testType: serverTest, name: "SigningHash-ServerKeyExchange-Fallback", config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithSHA1, @@ -4716,6 +4859,7 @@ func addSignatureAlgorithmTests() { testType: serverTest, name: "SigningHash-ClientAuth-Enforced", config: Config{ + MaxVersion: VersionTLS12, Certificates: []Certificate{rsaCertificate}, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithMD5, @@ -4737,6 +4881,7 @@ func addSignatureAlgorithmTests() { testCases = append(testCases, testCase{ name: "SigningHash-ServerKeyExchange-Enforced", config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithMD5, @@ -4751,9 +4896,12 @@ func addSignatureAlgorithmTests() { // Test that the agreed upon digest respects the client preferences and // the server digests. + // + // TODO(davidben): Add TLS 1.3 versions of these. testCases = append(testCases, testCase{ name: "Agree-Digest-Fallback", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithSHA512, @@ -4770,6 +4918,7 @@ func addSignatureAlgorithmTests() { testCases = append(testCases, testCase{ name: "Agree-Digest-SHA256", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithSHA1, @@ -4786,6 +4935,7 @@ func addSignatureAlgorithmTests() { testCases = append(testCases, testCase{ name: "Agree-Digest-SHA1", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithSHA1, @@ -4801,6 +4951,7 @@ func addSignatureAlgorithmTests() { testCases = append(testCases, testCase{ name: "Agree-Digest-Default", config: Config{ + MaxVersion: VersionTLS12, ClientAuth: RequireAnyClientCert, SignatureAlgorithms: []signatureAlgorithm{ signatureRSAPKCS1WithSHA256, @@ -4815,6 +4966,23 @@ func addSignatureAlgorithmTests() { }, expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256, }) + + // In TLS 1.2 and below, ECDSA uses the curve list rather than the + // signature algorithms. + // + // TODO(davidben): Add a TLS 1.3 version of this test where the mismatch + // is allowed. + testCases = append(testCases, testCase{ + name: "CheckLeafCurve", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{getECDSACertificate()}, + }, + flags: []string{"-p384-only"}, + shouldFail: true, + expectedError: ":BAD_ECC_CERT:", + }) } // timeouts is the retransmit schedule for BoringSSL. It doubles and @@ -4875,6 +5043,10 @@ func addDTLSRetransmitTests() { // shim must send flight N again, testing that the shim implements DTLS // retransmit on a timeout. + // TODO(davidben): Add TLS 1.3 versions of these tests. There will + // likely be more epochs to cross and the final message's retransmit may + // be more complex. + for _, async := range []bool{true, false} { var tests []testCase @@ -4886,6 +5058,7 @@ func addDTLSRetransmitTests() { protocol: dtls, name: "DTLS-Retransmit-Client-" + number, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: timeouts[:i], }, @@ -4897,6 +5070,7 @@ func addDTLSRetransmitTests() { testType: serverTest, name: "DTLS-Retransmit-Server-" + number, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: timeouts[:i], }, @@ -4911,6 +5085,7 @@ func addDTLSRetransmitTests() { protocol: dtls, name: "DTLS-Retransmit-Timeout", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: timeouts, }, @@ -4927,6 +5102,7 @@ func addDTLSRetransmitTests() { protocol: dtls, name: "DTLS-Retransmit-Fudge", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: []time.Duration{ timeouts[0] - 10*time.Millisecond, @@ -4944,6 +5120,7 @@ func addDTLSRetransmitTests() { protocol: dtls, name: "DTLS-Retransmit-Fragmented", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: []time.Duration{timeouts[0]}, MaxHandshakeRecordLength: 2, @@ -4956,6 +5133,7 @@ func addDTLSRetransmitTests() { protocol: dtls, name: "DTLS-Retransmit-Short-Client", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1], }, @@ -4968,6 +5146,7 @@ func addDTLSRetransmitTests() { testType: serverTest, name: "DTLS-Retransmit-Short-Server", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1], }, @@ -5069,6 +5248,7 @@ func addTLSUniqueTests() { name: "TLSUnique-" + suffix, testTLSUnique: true, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoExtendedMasterSecret: !hasEMS, }, @@ -5078,6 +5258,7 @@ func addTLSUniqueTests() { if isResumption { test.resumeSession = true test.resumeConfig = &Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ NoExtendedMasterSecret: !hasEMS, }, @@ -5099,6 +5280,7 @@ func addCustomExtensionTests() { expectedContents := "custom extension" emptyString := "" + // TODO(davidben): Add TLS 1.3 versions of these tests. for _, isClient := range []bool{false, true} { suffix := "Server" flag := "-enable-server-custom-extension" @@ -5113,6 +5295,7 @@ func addCustomExtensionTests() { testType: testType, name: "CustomExtensions-" + suffix, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ CustomExtension: expectedContents, ExpectedCustomExtension: &expectedContents, @@ -5126,6 +5309,7 @@ func addCustomExtensionTests() { testType: testType, name: "CustomExtensions-ParseError-" + suffix, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ CustomExtension: expectedContents + "foo", ExpectedCustomExtension: &expectedContents, @@ -5141,6 +5325,7 @@ func addCustomExtensionTests() { testType: testType, name: "CustomExtensions-FailAdd-" + suffix, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ CustomExtension: expectedContents, ExpectedCustomExtension: &expectedContents, @@ -5163,6 +5348,7 @@ func addCustomExtensionTests() { testType: testType, name: "CustomExtensions-Skip-" + suffix, config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ CustomExtension: skipCustomExtension, ExpectedCustomExtension: &emptyString, @@ -5178,6 +5364,7 @@ func addCustomExtensionTests() { testType: serverTest, name: "CustomExtensions-NotCalled-Server", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ ExpectedCustomExtension: &emptyString, }, @@ -5190,6 +5377,7 @@ func addCustomExtensionTests() { testType: clientTest, name: "UnknownExtension-Client", config: Config{ + MaxVersion: VersionTLS12, Bugs: ProtocolBugs{ CustomExtension: expectedContents, }, @@ -5231,10 +5419,12 @@ var testCurves = []struct { } func addCurveTests() { + // TODO(davidben): Add a TLS 1.3 versions of these tests. for _, curve := range testCurves { testCases = append(testCases, testCase{ name: "CurveTest-Client-" + curve.name, config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, CurvePreferences: []CurveID{curve.id}, }, @@ -5244,6 +5434,7 @@ func addCurveTests() { testType: serverTest, name: "CurveTest-Server-" + curve.name, config: Config{ + MaxVersion: VersionTLS12, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, CurvePreferences: []CurveID{curve.id}, }, @@ -5261,6 +5452,101 @@ func addCurveTests() { CurvePreferences: []CurveID{bogusCurve, CurveP256}, }, }) + + // The server must not consider ECDHE ciphers when there are no + // supported curves. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoSupportedCurves", + config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + NoSupportedCurves: true, + }, + }, + shouldFail: true, + expectedError: ":NO_SHARED_CIPHER:", + }) + + // The server must fall back to another cipher when there are no + // supported curves. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoCommonCurves", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + }, + CurvePreferences: []CurveID{CurveP224}, + }, + expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + }) + + // The client must reject bogus curves and disabled curves. + testCases = append(testCases, testCase{ + name: "BadECDHECurve", + config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SendCurve: bogusCurve, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "UnsupportedCurve", + config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + IgnorePeerCurvePreferences: true, + }, + }, + flags: []string{"-p384-only"}, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + // Test invalid curve points. + testCases = append(testCases, testCase{ + name: "InvalidECDHPoint-Client", + config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this test. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + InvalidECDHPoint: true, + }, + }, + shouldFail: true, + expectedError: ":INVALID_ENCODING:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "InvalidECDHPoint-Server", + config: Config{ + // TODO(davidben): Add a TLS 1.3 version of this test. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + InvalidECDHPoint: true, + }, + }, + shouldFail: true, + expectedError: ":INVALID_ENCODING:", + }) } func addCECPQ1Tests() { From cecee27c9943574b213d1e045573646365169f7b Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 13:33:47 -0400 Subject: [PATCH 16/40] Fix the Go code to be aware of DTLS version bounds. Right now I believe we are testing against DTLS 1.3 ClientHellos. Fix this in preparation for making VersionTLS13 go elsewhere in the Go code. Unfortunately, I made the mistake of mapping DTLS 1.0 to TLS 1.0 rather than 1.1 in Go. This does mean the names of the tests naturally work out correctly, but we have to deal with this awkward DTLS-1.1-shaped hole in our logic. Change-Id: I8715582ed90acc1f08197831cae6de8d5442d028 Reviewed-on: https://boringssl-review.googlesource.com/8562 Reviewed-by: David Benjamin --- ssl/test/runner/common.go | 49 ++++++++++++++++++++++------- ssl/test/runner/handshake_client.go | 10 +++--- ssl/test/runner/handshake_server.go | 2 +- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 1012810fd..95c546199 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -917,18 +917,40 @@ func (c *Config) cipherSuites() []uint16 { return s } -func (c *Config) minVersion() uint16 { - if c == nil || c.MinVersion == 0 { - return minVersion +func (c *Config) minVersion(isDTLS bool) uint16 { + ret := uint16(minVersion) + if c != nil && c.MinVersion != 0 { + ret = c.MinVersion } - return c.MinVersion + if isDTLS { + // The lowest version of DTLS is 1.0. There is no DSSL 3.0. + if ret < VersionTLS10 { + return VersionTLS10 + } + // There is no such thing as DTLS 1.1. + if ret == VersionTLS11 { + return VersionTLS12 + } + } + return ret } -func (c *Config) maxVersion() uint16 { - if c == nil || c.MaxVersion == 0 { - return maxVersion +func (c *Config) maxVersion(isDTLS bool) uint16 { + ret := uint16(maxVersion) + if c != nil && c.MaxVersion != 0 { + ret = c.MaxVersion } - return c.MaxVersion + if isDTLS { + // We only implement up to DTLS 1.2. + if ret > VersionTLS12 { + return VersionTLS12 + } + // There is no such thing as DTLS 1.1. + if ret == VersionTLS11 { + return VersionTLS10 + } + } + return ret } var defaultCurvePreferences = []CurveID{CurveX25519, CurveP256, CurveP384, CurveP521} @@ -942,9 +964,14 @@ func (c *Config) curvePreferences() []CurveID { // mutualVersion returns the protocol version to use given the advertised // version of the peer. -func (c *Config) mutualVersion(vers uint16) (uint16, bool) { - minVersion := c.minVersion() - maxVersion := c.maxVersion() +func (c *Config) mutualVersion(vers uint16, isDTLS bool) (uint16, bool) { + // There is no such thing as DTLS 1.1. + if isDTLS && vers == VersionTLS11 { + vers = VersionTLS10 + } + + minVersion := c.minVersion(isDTLS) + maxVersion := c.maxVersion(isDTLS) if vers < minVersion { return 0, false diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 62154b82a..825587229 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -57,7 +57,7 @@ func (c *Conn) clientHandshake() error { hello := &clientHelloMsg{ isDTLS: c.isDTLS, - vers: c.config.maxVersion(), + vers: c.config.maxVersion(c.isDTLS), compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), ocspStapling: true, @@ -71,7 +71,7 @@ func (c *Conn) clientHandshake() error { duplicateExtension: c.config.Bugs.DuplicateExtension, channelIDSupported: c.config.ChannelID != nil, npnLast: c.config.Bugs.SwapNPNAndALPN, - extendedMasterSecret: c.config.maxVersion() >= VersionTLS10, + extendedMasterSecret: c.config.maxVersion(c.isDTLS) >= VersionTLS10, srtpProtectionProfiles: c.config.SRTPProtectionProfiles, srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer, customExtension: c.config.Bugs.CustomExtension, @@ -169,8 +169,8 @@ NextCipherSuite: } } - versOk := candidateSession.vers >= c.config.minVersion() && - candidateSession.vers <= c.config.maxVersion() + versOk := candidateSession.vers >= c.config.minVersion(c.isDTLS) && + candidateSession.vers <= c.config.maxVersion(c.isDTLS) if ticketOk && versOk && cipherSuiteOk { session = candidateSession } @@ -267,7 +267,7 @@ NextCipherSuite: return unexpectedMessageError(serverHello, msg) } - c.vers, ok = c.config.mutualVersion(serverHello.vers) + c.vers, ok = c.config.mutualVersion(serverHello.vers, c.isDTLS) if !ok { c.sendAlert(alertProtocolVersion) return fmt.Errorf("tls: server selected unsupported protocol version %x", serverHello.vers) diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 7847acb5f..d10c72bac 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -211,7 +211,7 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { } } - c.vers, ok = config.mutualVersion(hs.clientHello.vers) + c.vers, ok = config.mutualVersion(hs.clientHello.vers, c.isDTLS) if !ok { c.sendAlert(alertProtocolVersion) return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers) From 24599a89c0a94676ff86b42e8b9465a479ccfd1e Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 18:56:53 -0400 Subject: [PATCH 17/40] Rename EncryptedExtensions in Go in preparation for TLS 1.3. TLS 1.3 defines its own EncryptedExtensions message. The existing one is for Channel ID which probably should not have tried to generalize itself. Change-Id: I4f48bece98510eb54e64fbf3df6c2a7332bc0261 Reviewed-on: https://boringssl-review.googlesource.com/8566 Reviewed-by: David Benjamin --- ssl/test/runner/common.go | 30 +++++++++++++-------------- ssl/test/runner/conn.go | 4 ++-- ssl/test/runner/handshake_client.go | 10 ++++----- ssl/test/runner/handshake_messages.go | 8 +++---- ssl/test/runner/handshake_server.go | 14 ++++++------- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 95c546199..3db5d7717 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -49,21 +49,21 @@ const ( // TLS handshake message types. const ( - typeHelloRequest uint8 = 0 - typeClientHello uint8 = 1 - typeServerHello uint8 = 2 - typeHelloVerifyRequest uint8 = 3 - typeNewSessionTicket uint8 = 4 - typeCertificate uint8 = 11 - typeServerKeyExchange uint8 = 12 - typeCertificateRequest uint8 = 13 - typeServerHelloDone uint8 = 14 - typeCertificateVerify uint8 = 15 - typeClientKeyExchange uint8 = 16 - typeFinished uint8 = 20 - typeCertificateStatus uint8 = 22 - typeNextProtocol uint8 = 67 // Not IANA assigned - typeEncryptedExtensions uint8 = 203 // Not IANA assigned + typeHelloRequest uint8 = 0 + typeClientHello uint8 = 1 + typeServerHello uint8 = 2 + typeHelloVerifyRequest uint8 = 3 + typeNewSessionTicket uint8 = 4 + typeCertificate uint8 = 11 + typeServerKeyExchange uint8 = 12 + typeCertificateRequest uint8 = 13 + typeServerHelloDone uint8 = 14 + typeCertificateVerify uint8 = 15 + typeClientKeyExchange uint8 = 16 + typeFinished uint8 = 20 + typeCertificateStatus uint8 = 22 + typeNextProtocol uint8 = 67 // Not IANA assigned + typeChannelID uint8 = 203 // Not IANA assigned ) // TLS compression types. diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index 8244a4c1c..6c127e6ae 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go @@ -1107,8 +1107,8 @@ func (c *Conn) readHandshake() (interface{}, error) { m = new(finishedMsg) case typeHelloVerifyRequest: m = new(helloVerifyRequestMsg) - case typeEncryptedExtensions: - m = new(encryptedExtensionsMsg) + case typeChannelID: + m = new(channelIDMsg) default: return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 825587229..95304d70a 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -880,7 +880,7 @@ func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error { } if hs.serverHello.channelIDRequested { - encryptedExtensions := new(encryptedExtensionsMsg) + channelIDMsg := new(channelIDMsg) if c.config.ChannelID.Curve != elliptic.P256() { return fmt.Errorf("tls: Channel ID is not on P-256.") } @@ -897,14 +897,14 @@ func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error { writeIntPadded(channelID[32:64], c.config.ChannelID.Y) writeIntPadded(channelID[64:96], r) writeIntPadded(channelID[96:128], s) - encryptedExtensions.channelID = channelID + channelIDMsg.channelID = channelID c.channelID = &c.config.ChannelID.PublicKey - encryptedExtensionsBytes := encryptedExtensions.marshal() - hs.writeHash(encryptedExtensionsBytes, seqno) + channelIDMsgBytes := channelIDMsg.marshal() + hs.writeHash(channelIDMsgBytes, seqno) seqno++ - postCCSBytes = append(postCCSBytes, encryptedExtensionsBytes...) + postCCSBytes = append(postCCSBytes, channelIDMsgBytes...) } finished := new(finishedMsg) diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 9637e9a09..907ae9cbe 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -1641,12 +1641,12 @@ func (m *helloVerifyRequestMsg) unmarshal(data []byte) bool { return true } -type encryptedExtensionsMsg struct { +type channelIDMsg struct { raw []byte channelID []byte } -func (m *encryptedExtensionsMsg) marshal() []byte { +func (m *channelIDMsg) marshal() []byte { if m.raw != nil { return m.raw } @@ -1654,7 +1654,7 @@ func (m *encryptedExtensionsMsg) marshal() []byte { length := 2 + 2 + len(m.channelID) x := make([]byte, 4+length) - x[0] = typeEncryptedExtensions + x[0] = typeChannelID x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) @@ -1667,7 +1667,7 @@ func (m *encryptedExtensionsMsg) marshal() []byte { return x } -func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { +func (m *channelIDMsg) unmarshal(data []byte) bool { if len(data) != 4+2+2+128 { return false } diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index d10c72bac..2d9db44d1 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -793,15 +793,15 @@ func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error { if err != nil { return err } - encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) + channelIDMsg, ok := msg.(*channelIDMsg) if !ok { c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(encryptedExtensions, msg) + return unexpectedMessageError(channelIDMsg, msg) } - x := new(big.Int).SetBytes(encryptedExtensions.channelID[0:32]) - y := new(big.Int).SetBytes(encryptedExtensions.channelID[32:64]) - r := new(big.Int).SetBytes(encryptedExtensions.channelID[64:96]) - s := new(big.Int).SetBytes(encryptedExtensions.channelID[96:128]) + x := new(big.Int).SetBytes(channelIDMsg.channelID[0:32]) + y := new(big.Int).SetBytes(channelIDMsg.channelID[32:64]) + r := new(big.Int).SetBytes(channelIDMsg.channelID[64:96]) + s := new(big.Int).SetBytes(channelIDMsg.channelID[96:128]) if !elliptic.P256().IsOnCurve(x, y) { return errors.New("tls: invalid channel ID public key") } @@ -815,7 +815,7 @@ func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error { } c.channelID = channelID - hs.writeClientHash(encryptedExtensions.marshal()) + hs.writeClientHash(channelIDMsg.marshal()) } msg, err := c.readHandshake() From cedff871bac1ea64b237a5ad22b7d28394c6b7f6 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Thu, 30 Jun 2016 18:55:18 -0400 Subject: [PATCH 18/40] Add TLS 1.3 constants from draft 13 to Go. Change-Id: I73c75da86ff911b05dacb1679e18e9b84f9df214 Reviewed-on: https://boringssl-review.googlesource.com/8568 Reviewed-by: David Benjamin --- ssl/test/runner/alert.go | 4 ++++ ssl/test/runner/common.go | 36 +++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ssl/test/runner/alert.go b/ssl/test/runner/alert.go index 7db682672..8144b5005 100644 --- a/ssl/test/runner/alert.go +++ b/ssl/test/runner/alert.go @@ -16,6 +16,7 @@ const ( const ( alertCloseNotify alert = 0 + alertEndOfEarlyData alert = 1 alertUnexpectedMessage alert = 10 alertBadRecordMAC alert = 20 alertDecryptionFailed alert = 21 @@ -38,10 +39,12 @@ const ( alertInternalError alert = 80 alertUserCanceled alert = 90 alertNoRenegotiation alert = 100 + alertMissingExtension alert = 109 ) var alertText = map[alert]string{ alertCloseNotify: "close notify", + alertEndOfEarlyData: "end of early data", alertUnexpectedMessage: "unexpected message", alertBadRecordMAC: "bad record MAC", alertDecryptionFailed: "decryption failed", @@ -63,6 +66,7 @@ var alertText = map[alert]string{ alertInternalError: "internal error", alertUserCanceled: "user canceled", alertNoRenegotiation: "no renegotiation", + alertMissingExtension: "missing extension", } func (e alert) String() string { diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 3db5d7717..0e5d9e444 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -49,21 +49,23 @@ const ( // TLS handshake message types. const ( - typeHelloRequest uint8 = 0 - typeClientHello uint8 = 1 - typeServerHello uint8 = 2 - typeHelloVerifyRequest uint8 = 3 - typeNewSessionTicket uint8 = 4 - typeCertificate uint8 = 11 - typeServerKeyExchange uint8 = 12 - typeCertificateRequest uint8 = 13 - typeServerHelloDone uint8 = 14 - typeCertificateVerify uint8 = 15 - typeClientKeyExchange uint8 = 16 - typeFinished uint8 = 20 - typeCertificateStatus uint8 = 22 - typeNextProtocol uint8 = 67 // Not IANA assigned - typeChannelID uint8 = 203 // Not IANA assigned + typeHelloRequest uint8 = 0 + typeClientHello uint8 = 1 + typeServerHello uint8 = 2 + typeHelloVerifyRequest uint8 = 3 + typeNewSessionTicket uint8 = 4 + typeHelloRetryRequest uint8 = 6 // draft-ietf-tls-tls13-13 + typeEncryptedExtensions uint8 = 8 // draft-ietf-tls-tls13-13 + typeCertificate uint8 = 11 + typeServerKeyExchange uint8 = 12 + typeCertificateRequest uint8 = 13 + typeServerHelloDone uint8 = 14 + typeCertificateVerify uint8 = 15 + typeClientKeyExchange uint8 = 16 + typeFinished uint8 = 20 + typeCertificateStatus uint8 = 22 + typeNextProtocol uint8 = 67 // Not IANA assigned + typeChannelID uint8 = 203 // Not IANA assigned ) // TLS compression types. @@ -83,6 +85,10 @@ const ( extensionSignedCertificateTimestamp uint16 = 18 extensionExtendedMasterSecret uint16 = 23 extensionSessionTicket uint16 = 35 + extensionKeyShare uint16 = 40 // draft-ietf-tls-tls13-13 + extensionPreSharedKey uint16 = 41 // draft-ietf-tls-tls13-13 + extensionEarlyData uint16 = 42 // draft-ietf-tls-tls13-13 + extensionCookie uint16 = 44 // draft-ietf-tls-tls13-13 extensionCustom uint16 = 1234 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionRenegotiationInfo uint16 = 0xff01 From d94b83bb378a7e09eecf73fa77cea64b9646867e Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 6 Jul 2016 13:50:14 -0700 Subject: [PATCH 19/40] Rename Channel ID's EncryptedExtensions to just ChannelID in C. To match the Go side. That message will never be used for anything else, so there's not much need to give it such a long name. Change-Id: I3396c9d513d02d873e59cd8e81ee64005c5c706c Reviewed-on: https://boringssl-review.googlesource.com/8620 Reviewed-by: Steven Valdez Reviewed-by: David Benjamin --- include/openssl/ssl3.h | 2 +- ssl/handshake_client.c | 3 +-- ssl/handshake_server.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index 848d74aec..ed49362dc 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -404,7 +404,7 @@ OPENSSL_COMPILE_ASSERT( #define SSL3_MT_CERTIFICATE_STATUS 22 #define SSL3_MT_SUPPLEMENTAL_DATA 23 #define SSL3_MT_NEXT_PROTO 67 -#define SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS 203 +#define SSL3_MT_CHANNEL_ID 203 #define DTLS1_MT_HELLO_VERIFY_REQUEST 3 /* The following are legacy aliases for consumers which use diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c index e3cd10229..2ac7dee33 100644 --- a/ssl/handshake_client.c +++ b/ssl/handshake_client.c @@ -1987,8 +1987,7 @@ static int ssl3_send_channel_id(SSL *ssl) { } CBB cbb, body, child; - if (!ssl->method->init_message(ssl, &cbb, &body, - SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS) || + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) || !CBB_add_u16(&body, TLSEXT_TYPE_channel_id) || !CBB_add_u16_length_prefixed(&body, &child) || !BN_bn2cbb_padded(&child, 32, x) || !BN_bn2cbb_padded(&child, 32, y) || diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c index d302f1154..ebd47e10f 100644 --- a/ssl/handshake_server.c +++ b/ssl/handshake_server.c @@ -2029,7 +2029,7 @@ static int ssl3_get_channel_id(SSL *ssl) { BIGNUM x, y; CBS encrypted_extensions, extension; - n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS, + n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID, ssl_dont_hash_message, &ok); if (!ok) { From 8dda5cc904012c40ea4566ec135d638fbc0ab59e Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Thu, 30 Jun 2016 18:51:11 -0400 Subject: [PATCH 20/40] Add a Go version of CBB and convert ClientHello marshaling to it. [Originally written by nharper and then slightly tweaked by davidben.] Between the new deeply nested extension (KeyShare) and most of ServerHello extensions moving to a separate message, this is probably long overdue. Change-Id: Ia86e30f56b597471bb7e27d726a9ec92687b4d10 Reviewed-on: https://boringssl-review.googlesource.com/8569 Reviewed-by: David Benjamin --- ssl/test/runner/handshake_messages.go | 425 +++++++++++--------------- 1 file changed, 186 insertions(+), 239 deletions(-) diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 907ae9cbe..2dff77e48 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -6,6 +6,105 @@ package runner import "bytes" +func writeLen(buf []byte, v, size int) { + for i := 0; i < size; i++ { + buf[size-i-1] = byte(v) + v >>= 8 + } + if v != 0 { + panic("length is too long") + } +} + +type byteBuilder struct { + buf *[]byte + start int + prefixLen int + child *byteBuilder +} + +func newByteBuilder() *byteBuilder { + buf := make([]byte, 0, 32) + return &byteBuilder{buf: &buf} +} + +func (bb *byteBuilder) len() int { + return len(*bb.buf) - bb.start - bb.prefixLen +} + +func (bb *byteBuilder) flush() { + if bb.child == nil { + return + } + bb.child.flush() + writeLen((*bb.buf)[bb.child.start:], bb.child.len(), bb.child.prefixLen) + bb.child = nil + return +} + +func (bb *byteBuilder) finish() []byte { + bb.flush() + return *bb.buf +} + +func (bb *byteBuilder) addU8(u uint8) { + bb.flush() + *bb.buf = append(*bb.buf, u) +} + +func (bb *byteBuilder) addU16(u uint16) { + bb.flush() + *bb.buf = append(*bb.buf, byte(u>>8), byte(u)) +} + +func (bb *byteBuilder) addU24(u int) { + bb.flush() + *bb.buf = append(*bb.buf, byte(u>>16), byte(u>>8), byte(u)) +} + +func (bb *byteBuilder) addU32(u uint32) { + bb.flush() + *bb.buf = append(*bb.buf, byte(u>>24), byte(u>>16), byte(u>>8), byte(u)) +} + +func (bb *byteBuilder) addU8LengthPrefixed() *byteBuilder { + return bb.createChild(1) +} + +func (bb *byteBuilder) addU16LengthPrefixed() *byteBuilder { + return bb.createChild(2) +} + +func (bb *byteBuilder) addU24LengthPrefixed() *byteBuilder { + return bb.createChild(3) +} + +func (bb *byteBuilder) addBytes(b []byte) { + bb.flush() + *bb.buf = append(*bb.buf, b...) +} + +func (bb *byteBuilder) createChild(lengthPrefixSize int) *byteBuilder { + bb.flush() + bb.child = &byteBuilder{ + buf: bb.buf, + start: len(*bb.buf), + prefixLen: lengthPrefixSize, + } + for i := 0; i < lengthPrefixSize; i++ { + *bb.buf = append(*bb.buf, 0) + } + return bb.child +} + +func (bb *byteBuilder) discardChild() { + if bb.child != nil { + return + } + bb.child = nil + *bb.buf = (*bb.buf)[:bb.start] +} + type clientHelloMsg struct { raw []byte isDTLS bool @@ -75,132 +174,38 @@ func (m *clientHelloMsg) marshal() []byte { return m.raw } - length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) - if m.isDTLS { - length += 1 + len(m.cookie) - } - numExtensions := 0 - extensionsLength := 0 - if m.nextProtoNeg { - numExtensions++ - } - if m.ocspStapling { - extensionsLength += 1 + 2 + 2 - numExtensions++ - } - if len(m.serverName) > 0 { - extensionsLength += 5 + len(m.serverName) - numExtensions++ - } - if len(m.supportedCurves) > 0 { - extensionsLength += 2 + 2*len(m.supportedCurves) - numExtensions++ - } - if len(m.supportedPoints) > 0 { - extensionsLength += 1 + len(m.supportedPoints) - numExtensions++ - } - if m.ticketSupported { - extensionsLength += len(m.sessionTicket) - numExtensions++ - } - if len(m.signatureAlgorithms) > 0 { - extensionsLength += 2 + 2*len(m.signatureAlgorithms) - numExtensions++ - } - if m.secureRenegotiation != nil { - extensionsLength += 1 + len(m.secureRenegotiation) - numExtensions++ - } - if m.duplicateExtension { - numExtensions += 2 - } - if m.channelIDSupported { - numExtensions++ - } - if len(m.alpnProtocols) > 0 { - extensionsLength += 2 - for _, s := range m.alpnProtocols { - if l := len(s); l > 255 { - panic("invalid ALPN protocol") - } - extensionsLength++ - extensionsLength += len(s) - } - numExtensions++ - } - if m.extendedMasterSecret { - numExtensions++ - } - if len(m.srtpProtectionProfiles) > 0 { - extensionsLength += 2 + 2*len(m.srtpProtectionProfiles) - extensionsLength += 1 + len(m.srtpMasterKeyIdentifier) - numExtensions++ - } - if m.sctListSupported { - numExtensions++ - } - if l := len(m.customExtension); l > 0 { - extensionsLength += l - numExtensions++ - } - if numExtensions > 0 { - extensionsLength += 4 * numExtensions - length += 2 + extensionsLength - } - - x := make([]byte, 4+length) - x[0] = typeClientHello - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) + handshakeMsg := newByteBuilder() + handshakeMsg.addU8(typeClientHello) + hello := handshakeMsg.addU24LengthPrefixed() vers := versionToWire(m.vers, m.isDTLS) - x[4] = uint8(vers >> 8) - x[5] = uint8(vers) - copy(x[6:38], m.random) - x[38] = uint8(len(m.sessionId)) - copy(x[39:39+len(m.sessionId)], m.sessionId) - y := x[39+len(m.sessionId):] + hello.addU16(vers) + hello.addBytes(m.random) + sessionId := hello.addU8LengthPrefixed() + sessionId.addBytes(m.sessionId) if m.isDTLS { - y[0] = uint8(len(m.cookie)) - copy(y[1:], m.cookie) - y = y[1+len(m.cookie):] + cookie := hello.addU8LengthPrefixed() + cookie.addBytes(m.cookie) } - y[0] = uint8(len(m.cipherSuites) >> 7) - y[1] = uint8(len(m.cipherSuites) << 1) - for i, suite := range m.cipherSuites { - y[2+i*2] = uint8(suite >> 8) - y[3+i*2] = uint8(suite) + cipherSuites := hello.addU16LengthPrefixed() + for _, suite := range m.cipherSuites { + cipherSuites.addU16(suite) } - z := y[2+len(m.cipherSuites)*2:] - z[0] = uint8(len(m.compressionMethods)) - copy(z[1:], m.compressionMethods) + compressionMethods := hello.addU8LengthPrefixed() + compressionMethods.addBytes(m.compressionMethods) - z = z[1+len(m.compressionMethods):] - if numExtensions > 0 { - z[0] = byte(extensionsLength >> 8) - z[1] = byte(extensionsLength) - z = z[2:] - } + extensions := hello.addU16LengthPrefixed() if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - z[0] = 0xff - z[1] = 0xff - z = z[4:] + extensions.addU16(0xffff) + extensions.addU16(0) // 0-length for empty extension } if m.nextProtoNeg && !m.npnLast { - z[0] = byte(extensionNextProtoNeg >> 8) - z[1] = byte(extensionNextProtoNeg & 0xff) - // The length is always 0 - z = z[4:] + extensions.addU16(extensionNextProtoNeg) + extensions.addU16(0) // The length is always 0 } if len(m.serverName) > 0 { - z[0] = byte(extensionServerName >> 8) - z[1] = byte(extensionServerName & 0xff) - l := len(m.serverName) + 5 - z[2] = byte(l >> 8) - z[3] = byte(l) - z = z[4:] + extensions.addU16(extensionServerName) + serverNameList := extensions.addU16LengthPrefixed() // RFC 3546, section 3.1 // @@ -221,179 +226,121 @@ func (m *clientHelloMsg) marshal() []byte { // ServerName server_name_list<1..2^16-1> // } ServerNameList; - z[0] = byte((len(m.serverName) + 3) >> 8) - z[1] = byte(len(m.serverName) + 3) - z[3] = byte(len(m.serverName) >> 8) - z[4] = byte(len(m.serverName)) - copy(z[5:], []byte(m.serverName)) - z = z[l:] + serverName := serverNameList.addU16LengthPrefixed() + serverName.addU8(0) // NameType host_name(0) + hostName := serverName.addU16LengthPrefixed() + hostName.addBytes([]byte(m.serverName)) } if m.ocspStapling { + extensions.addU16(extensionStatusRequest) + certificateStatusRequest := extensions.addU16LengthPrefixed() + // RFC 4366, section 3.6 - z[0] = byte(extensionStatusRequest >> 8) - z[1] = byte(extensionStatusRequest) - z[2] = 0 - z[3] = 5 - z[4] = 1 // OCSP type + certificateStatusRequest.addU8(1) // OCSP type // Two zero valued uint16s for the two lengths. - z = z[9:] + certificateStatusRequest.addU16(0) // ResponderID length + certificateStatusRequest.addU16(0) // Extensions length } if len(m.supportedCurves) > 0 { - // http://tools.ietf.org/html/rfc4492#section-5.5.1 - z[0] = byte(extensionSupportedCurves >> 8) - z[1] = byte(extensionSupportedCurves) - l := 2 + 2*len(m.supportedCurves) - z[2] = byte(l >> 8) - z[3] = byte(l) - l -= 2 - z[4] = byte(l >> 8) - z[5] = byte(l) - z = z[6:] + // http://tools.ietf.org/html/rfc4492#section-5.1.1 + extensions.addU16(extensionSupportedCurves) + supportedCurvesList := extensions.addU16LengthPrefixed() + supportedCurves := supportedCurvesList.addU16LengthPrefixed() for _, curve := range m.supportedCurves { - z[0] = byte(curve >> 8) - z[1] = byte(curve) - z = z[2:] + supportedCurves.addU16(uint16(curve)) } } if len(m.supportedPoints) > 0 { - // http://tools.ietf.org/html/rfc4492#section-5.5.2 - z[0] = byte(extensionSupportedPoints >> 8) - z[1] = byte(extensionSupportedPoints) - l := 1 + len(m.supportedPoints) - z[2] = byte(l >> 8) - z[3] = byte(l) - l-- - z[4] = byte(l) - z = z[5:] + // http://tools.ietf.org/html/rfc4492#section-5.1.2 + extensions.addU16(extensionSupportedPoints) + supportedPointsList := extensions.addU16LengthPrefixed() + supportedPoints := supportedPointsList.addU8LengthPrefixed() for _, pointFormat := range m.supportedPoints { - z[0] = byte(pointFormat) - z = z[1:] + supportedPoints.addU8(pointFormat) } } if m.ticketSupported { // http://tools.ietf.org/html/rfc5077#section-3.2 - z[0] = byte(extensionSessionTicket >> 8) - z[1] = byte(extensionSessionTicket) - l := len(m.sessionTicket) - z[2] = byte(l >> 8) - z[3] = byte(l) - z = z[4:] - copy(z, m.sessionTicket) - z = z[len(m.sessionTicket):] + extensions.addU16(extensionSessionTicket) + sessionTicketExtension := extensions.addU16LengthPrefixed() + sessionTicketExtension.addBytes(m.sessionTicket) } if len(m.signatureAlgorithms) > 0 { // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 - z[0] = byte(extensionSignatureAlgorithms >> 8) - z[1] = byte(extensionSignatureAlgorithms) - l := 2 + 2*len(m.signatureAlgorithms) - z[2] = byte(l >> 8) - z[3] = byte(l) - z = z[4:] - - l -= 2 - z[0] = byte(l >> 8) - z[1] = byte(l) - z = z[2:] + extensions.addU16(extensionSignatureAlgorithms) + signatureAlgorithmsExtension := extensions.addU16LengthPrefixed() + signatureAlgorithms := signatureAlgorithmsExtension.addU16LengthPrefixed() for _, sigAlg := range m.signatureAlgorithms { - z[0] = byte(sigAlg >> 8) - z[1] = byte(sigAlg) - z = z[2:] + signatureAlgorithms.addU16(uint16(sigAlg)) } } if m.secureRenegotiation != nil { - z[0] = byte(extensionRenegotiationInfo >> 8) - z[1] = byte(extensionRenegotiationInfo & 0xff) - z[2] = 0 - z[3] = byte(1 + len(m.secureRenegotiation)) - z[4] = byte(len(m.secureRenegotiation)) - z = z[5:] - copy(z, m.secureRenegotiation) - z = z[len(m.secureRenegotiation):] + extensions.addU16(extensionRenegotiationInfo) + secureRenegoExt := extensions.addU16LengthPrefixed() + secureRenego := secureRenegoExt.addU8LengthPrefixed() + secureRenego.addBytes(m.secureRenegotiation) } if len(m.alpnProtocols) > 0 { - z[0] = byte(extensionALPN >> 8) - z[1] = byte(extensionALPN & 0xff) - lengths := z[2:] - z = z[6:] + // https://tools.ietf.org/html/rfc7301#section-3.1 + extensions.addU16(extensionALPN) + alpnExtension := extensions.addU16LengthPrefixed() - stringsLength := 0 + protocolNameList := alpnExtension.addU16LengthPrefixed() for _, s := range m.alpnProtocols { - l := len(s) - z[0] = byte(l) - copy(z[1:], s) - z = z[1+l:] - stringsLength += 1 + l + protocolName := protocolNameList.addU8LengthPrefixed() + protocolName.addBytes([]byte(s)) } - - lengths[2] = byte(stringsLength >> 8) - lengths[3] = byte(stringsLength) - stringsLength += 2 - lengths[0] = byte(stringsLength >> 8) - lengths[1] = byte(stringsLength) } if m.channelIDSupported { - z[0] = byte(extensionChannelID >> 8) - z[1] = byte(extensionChannelID & 0xff) - z = z[4:] + extensions.addU16(extensionChannelID) + extensions.addU16(0) // Length is always 0 } if m.nextProtoNeg && m.npnLast { - z[0] = byte(extensionNextProtoNeg >> 8) - z[1] = byte(extensionNextProtoNeg & 0xff) - // The length is always 0 - z = z[4:] + extensions.addU16(extensionNextProtoNeg) + extensions.addU16(0) // Length is always 0 } if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - z[0] = 0xff - z[1] = 0xff - z = z[4:] + extensions.addU16(0xffff) + extensions.addU16(0) } if m.extendedMasterSecret { // https://tools.ietf.org/html/rfc7627 - z[0] = byte(extensionExtendedMasterSecret >> 8) - z[1] = byte(extensionExtendedMasterSecret & 0xff) - z = z[4:] + extensions.addU16(extensionExtendedMasterSecret) + extensions.addU16(0) // Length is always 0 } if len(m.srtpProtectionProfiles) > 0 { - z[0] = byte(extensionUseSRTP >> 8) - z[1] = byte(extensionUseSRTP & 0xff) + // https://tools.ietf.org/html/rfc5764#section-4.1.1 + extensions.addU16(extensionUseSRTP) + useSrtpExt := extensions.addU16LengthPrefixed() - profilesLen := 2 * len(m.srtpProtectionProfiles) - mkiLen := len(m.srtpMasterKeyIdentifier) - l := 2 + profilesLen + 1 + mkiLen - z[2] = byte(l >> 8) - z[3] = byte(l & 0xff) - - z[4] = byte(profilesLen >> 8) - z[5] = byte(profilesLen & 0xff) - z = z[6:] + srtpProtectionProfiles := useSrtpExt.addU16LengthPrefixed() for _, p := range m.srtpProtectionProfiles { - z[0] = byte(p >> 8) - z[1] = byte(p & 0xff) - z = z[2:] + // An SRTPProtectionProfile is defined as uint8[2], + // not uint16. For some reason, we're storing it + // as a uint16. + srtpProtectionProfiles.addU8(byte(p >> 8)) + srtpProtectionProfiles.addU8(byte(p)) } - - z[0] = byte(mkiLen) - copy(z[1:], []byte(m.srtpMasterKeyIdentifier)) - z = z[1+mkiLen:] + srtpMki := useSrtpExt.addU8LengthPrefixed() + srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier)) } if m.sctListSupported { - z[0] = byte(extensionSignedCertificateTimestamp >> 8) - z[1] = byte(extensionSignedCertificateTimestamp & 0xff) - z = z[4:] + extensions.addU16(extensionSignedCertificateTimestamp) + extensions.addU16(0) // Length is always 0 } if l := len(m.customExtension); l > 0 { - z[0] = byte(extensionCustom >> 8) - z[1] = byte(extensionCustom & 0xff) - z[2] = byte(l >> 8) - z[3] = byte(l & 0xff) - copy(z[4:], []byte(m.customExtension)) - z = z[4+l:] + extensions.addU16(extensionCustom) + customExt := extensions.addU16LengthPrefixed() + customExt.addBytes([]byte(m.customExtension)) } - m.raw = x + if extensions.len() == 0 { + hello.discardChild() + } - return x + m.raw = handshakeMsg.finish() + return m.raw } func (m *clientHelloMsg) unmarshal(data []byte) bool { From 5212ef8b3df9a07f45b6f6ed73b89612f71ea96a Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Thu, 30 Jun 2016 19:26:07 -0400 Subject: [PATCH 21/40] Reimplement serverHelloMsg with byteBuilder in Go. [Originally written by nharper and tweaked by davidben.] This will end up being split in two with most of the ServerHello extensions being serializable in both ServerHello and EncryptedExtensions depending on version. Change-Id: Ida5876d55fbafb982bc2e5fdaf82872e733d6536 Reviewed-on: https://boringssl-review.googlesource.com/8580 Reviewed-by: David Benjamin --- ssl/test/runner/handshake_messages.go | 230 +++++++------------------- 1 file changed, 64 insertions(+), 166 deletions(-) diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 2dff77e48..b4f0c5b54 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -604,211 +604,109 @@ func (m *serverHelloMsg) marshal() []byte { return m.raw } - length := 38 + len(m.sessionId) - numExtensions := 0 - extensionsLength := 0 - - nextProtoLen := 0 - if m.nextProtoNeg { - numExtensions++ - for _, v := range m.nextProtos { - nextProtoLen += len(v) - } - nextProtoLen += len(m.nextProtos) - extensionsLength += nextProtoLen - } - if m.ocspStapling { - numExtensions++ - } - if m.ticketSupported { - numExtensions++ - } - if m.secureRenegotiation != nil { - extensionsLength += 1 + len(m.secureRenegotiation) - numExtensions++ - } - if m.duplicateExtension { - numExtensions += 2 - } - if m.channelIDRequested { - numExtensions++ - } - if alpnLen := len(m.alpnProtocol); alpnLen > 0 || m.alpnProtocolEmpty { - if alpnLen >= 256 { - panic("invalid ALPN protocol") - } - extensionsLength += 2 + 1 + alpnLen - numExtensions++ - } - if m.extendedMasterSecret { - numExtensions++ - } - if m.srtpProtectionProfile != 0 { - extensionsLength += 2 + 2 + 1 + len(m.srtpMasterKeyIdentifier) - numExtensions++ - } - if m.sctList != nil { - extensionsLength += len(m.sctList) - numExtensions++ - } - if l := len(m.customExtension); l > 0 { - extensionsLength += l - numExtensions++ - } - - if numExtensions > 0 { - extensionsLength += 4 * numExtensions - length += 2 + extensionsLength - } - - x := make([]byte, 4+length) - x[0] = typeServerHello - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) + handshakeMsg := newByteBuilder() + handshakeMsg.addU8(typeServerHello) + hello := handshakeMsg.addU24LengthPrefixed() vers := versionToWire(m.vers, m.isDTLS) - x[4] = uint8(vers >> 8) - x[5] = uint8(vers) - copy(x[6:38], m.random) - x[38] = uint8(len(m.sessionId)) - copy(x[39:39+len(m.sessionId)], m.sessionId) - z := x[39+len(m.sessionId):] - z[0] = uint8(m.cipherSuite >> 8) - z[1] = uint8(m.cipherSuite) - z[2] = uint8(m.compressionMethod) + hello.addU16(vers) + hello.addBytes(m.random) + sessionId := hello.addU8LengthPrefixed() + sessionId.addBytes(m.sessionId) + hello.addU16(m.cipherSuite) + hello.addU8(m.compressionMethod) - z = z[3:] - if numExtensions > 0 { - z[0] = byte(extensionsLength >> 8) - z[1] = byte(extensionsLength) - z = z[2:] - } + extensions := hello.addU16LengthPrefixed() if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - z[0] = 0xff - z[1] = 0xff - z = z[4:] + extensions.addU16(0xffff) + extensions.addU16(0) // length = 0 for empty extension } if m.nextProtoNeg && !m.npnLast { - z[0] = byte(extensionNextProtoNeg >> 8) - z[1] = byte(extensionNextProtoNeg & 0xff) - z[2] = byte(nextProtoLen >> 8) - z[3] = byte(nextProtoLen) - z = z[4:] + extensions.addU16(extensionNextProtoNeg) + extension := extensions.addU16LengthPrefixed() for _, v := range m.nextProtos { - l := len(v) - if l > 255 { - l = 255 + if len(v) > 255 { + v = v[:255] } - z[0] = byte(l) - copy(z[1:], []byte(v[0:l])) - z = z[1+l:] + npn := extension.addU8LengthPrefixed() + npn.addBytes([]byte(v)) } } if m.ocspStapling { - z[0] = byte(extensionStatusRequest >> 8) - z[1] = byte(extensionStatusRequest) - z = z[4:] + extensions.addU16(extensionStatusRequest) + extensions.addU16(0) } if m.ticketSupported { - z[0] = byte(extensionSessionTicket >> 8) - z[1] = byte(extensionSessionTicket) - z = z[4:] + extensions.addU16(extensionSessionTicket) + extensions.addU16(0) } if m.secureRenegotiation != nil { - z[0] = byte(extensionRenegotiationInfo >> 8) - z[1] = byte(extensionRenegotiationInfo & 0xff) - z[2] = 0 - z[3] = byte(1 + len(m.secureRenegotiation)) - z[4] = byte(len(m.secureRenegotiation)) - z = z[5:] - copy(z, m.secureRenegotiation) - z = z[len(m.secureRenegotiation):] + extensions.addU16(extensionRenegotiationInfo) + extension := extensions.addU16LengthPrefixed() + secureRenego := extension.addU8LengthPrefixed() + secureRenego.addBytes(m.secureRenegotiation) } - if alpnLen := len(m.alpnProtocol); alpnLen > 0 || m.alpnProtocolEmpty { - z[0] = byte(extensionALPN >> 8) - z[1] = byte(extensionALPN & 0xff) - l := 2 + 1 + alpnLen - z[2] = byte(l >> 8) - z[3] = byte(l) - l -= 2 - z[4] = byte(l >> 8) - z[5] = byte(l) - l -= 1 - z[6] = byte(l) - copy(z[7:], []byte(m.alpnProtocol)) - z = z[7+alpnLen:] + if len(m.alpnProtocol) > 0 || m.alpnProtocolEmpty { + extensions.addU16(extensionALPN) + extension := extensions.addU16LengthPrefixed() + + protocolNameList := extension.addU16LengthPrefixed() + protocolName := protocolNameList.addU8LengthPrefixed() + protocolName.addBytes([]byte(m.alpnProtocol)) } if m.channelIDRequested { - z[0] = byte(extensionChannelID >> 8) - z[1] = byte(extensionChannelID & 0xff) - z = z[4:] + extensions.addU16(extensionChannelID) + extensions.addU16(0) } if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. - z[0] = 0xff - z[1] = 0xff - z = z[4:] + extensions.addU16(0xffff) + extensions.addU16(0) } if m.extendedMasterSecret { - z[0] = byte(extensionExtendedMasterSecret >> 8) - z[1] = byte(extensionExtendedMasterSecret & 0xff) - z = z[4:] + extensions.addU16(extensionExtendedMasterSecret) + extensions.addU16(0) } if m.srtpProtectionProfile != 0 { - z[0] = byte(extensionUseSRTP >> 8) - z[1] = byte(extensionUseSRTP & 0xff) - l := 2 + 2 + 1 + len(m.srtpMasterKeyIdentifier) - z[2] = byte(l >> 8) - z[3] = byte(l & 0xff) - z[4] = 0 - z[5] = 2 - z[6] = byte(m.srtpProtectionProfile >> 8) - z[7] = byte(m.srtpProtectionProfile & 0xff) - l = len(m.srtpMasterKeyIdentifier) - z[8] = byte(l) - copy(z[9:], []byte(m.srtpMasterKeyIdentifier)) - z = z[9+l:] + extensions.addU16(extensionUseSRTP) + extension := extensions.addU16LengthPrefixed() + + srtpProtectionProfiles := extension.addU16LengthPrefixed() + srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile >> 8)) + srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile)) + srtpMki := extension.addU8LengthPrefixed() + srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier)) } if m.sctList != nil { - z[0] = byte(extensionSignedCertificateTimestamp >> 8) - z[1] = byte(extensionSignedCertificateTimestamp & 0xff) - l := len(m.sctList) - z[2] = byte(l >> 8) - z[3] = byte(l & 0xff) - copy(z[4:], m.sctList) - z = z[4+l:] + extensions.addU16(extensionSignedCertificateTimestamp) + extension := extensions.addU16LengthPrefixed() + extension.addBytes(m.sctList) } if l := len(m.customExtension); l > 0 { - z[0] = byte(extensionCustom >> 8) - z[1] = byte(extensionCustom & 0xff) - z[2] = byte(l >> 8) - z[3] = byte(l & 0xff) - copy(z[4:], []byte(m.customExtension)) - z = z[4+l:] + extensions.addU16(extensionCustom) + customExt := extensions.addU16LengthPrefixed() + customExt.addBytes([]byte(m.customExtension)) } if m.nextProtoNeg && m.npnLast { - z[0] = byte(extensionNextProtoNeg >> 8) - z[1] = byte(extensionNextProtoNeg & 0xff) - z[2] = byte(nextProtoLen >> 8) - z[3] = byte(nextProtoLen) - z = z[4:] + extensions.addU16(extensionNextProtoNeg) + extension := extensions.addU16LengthPrefixed() for _, v := range m.nextProtos { - l := len(v) - if l > 255 { - l = 255 + if len(v) > 255 { + v = v[0:255] } - z[0] = byte(l) - copy(z[1:], []byte(v[0:l])) - z = z[1+l:] + npn := extension.addU8LengthPrefixed() + npn.addBytes([]byte(v)) } } - m.raw = x + if extensions.len() == 0 { + hello.discardChild() + } - return x + m.raw = handshakeMsg.finish() + return m.raw } func (m *serverHelloMsg) unmarshal(data []byte) bool { From 17017769084cc5700b4b69ee45d9fe1e3c2d5d3f Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 20:17:53 -0400 Subject: [PATCH 22/40] Clarify how Quotient tests do rounding. Change-Id: Ifea17a894065cce607845208c96e2092e4632d61 Reviewed-on: https://boringssl-review.googlesource.com/8607 Reviewed-by: Adam Langley --- crypto/bn/bn_tests.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/bn/bn_tests.txt b/crypto/bn/bn_tests.txt index 17b7088d6..1be8848ff 100644 --- a/crypto/bn/bn_tests.txt +++ b/crypto/bn/bn_tests.txt @@ -5957,7 +5957,8 @@ B = -542fb814f45924aa09a16f2a6 # Quotient tests. # -# These test vectors satisfy Quotient = A / B and Remainder = A - B * Quotient. +# These test vectors satisfy Quotient = A / B, rounded towards zero, and +# Remainder = A - B * Quotient. Quotient = 1 Remainder = 0 From b3d51be52ffe753a8964a6175f57b5db17425a00 Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Fri, 1 Jul 2016 11:43:18 -0400 Subject: [PATCH 23/40] Split ServerHello extensions into a separate struct. [Originally written by nharper, tweaked by davidben.] In TLS 1.3, every extension the server previously sent gets moved to a separate EncryptedExtensions message. To be able to share code between the two, parse those extensions separately. For now, the handshake reads from serverHello.extensions.foo, though later much of the extensions logic will probably handle serverExtensions independent of whether it resides in ServerHello or EncryptedExtensions. Change-Id: I07aaae6df3ef6fbac49e64661d14078d0dbeafb0 Reviewed-on: https://boringssl-review.googlesource.com/8584 Reviewed-by: David Benjamin --- ssl/test/runner/handshake_client.go | 44 ++++---- ssl/test/runner/handshake_messages.go | 154 ++++++++++++++------------ ssl/test/runner/handshake_server.go | 60 +++++----- 3 files changed, 136 insertions(+), 122 deletions(-) diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 95304d70a..d3644f5d9 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -280,7 +280,7 @@ NextCipherSuite: return fmt.Errorf("tls: server selected an unsupported cipher suite") } - if c.config.Bugs.RequireRenegotiationInfo && serverHello.secureRenegotiation == nil { + if c.config.Bugs.RequireRenegotiationInfo && serverHello.extensions.secureRenegotiation == nil { return errors.New("tls: renegotiation extension missing") } @@ -288,15 +288,15 @@ NextCipherSuite: var expectedRenegInfo []byte expectedRenegInfo = append(expectedRenegInfo, c.clientVerify...) expectedRenegInfo = append(expectedRenegInfo, c.serverVerify...) - if !bytes.Equal(serverHello.secureRenegotiation, expectedRenegInfo) { + if !bytes.Equal(serverHello.extensions.secureRenegotiation, expectedRenegInfo) { c.sendAlert(alertHandshakeFailure) return fmt.Errorf("tls: renegotiation mismatch") } } if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil { - if serverHello.customExtension != *expected { - return fmt.Errorf("tls: bad custom extension contents %q", serverHello.customExtension) + if serverHello.extensions.customExtension != *expected { + return fmt.Errorf("tls: bad custom extension contents %q", serverHello.extensions.customExtension) } } @@ -441,7 +441,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { c.peerCertificates = certs } - if hs.serverHello.ocspStapling { + if hs.serverHello.extensions.ocspStapling { msg, err := c.readHandshake() if err != nil { return err @@ -596,7 +596,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { c.writeRecord(recordTypeHandshake, ckx.marshal()) } - if hs.serverHello.extendedMasterSecret && c.vers >= VersionTLS10 { + if hs.serverHello.extensions.extendedMasterSecret && c.vers >= VersionTLS10 { hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash) c.extendedMasterSecret = true } else { @@ -701,8 +701,8 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { clientDidNPN := hs.hello.nextProtoNeg clientDidALPN := len(hs.hello.alpnProtocols) > 0 - serverHasNPN := hs.serverHello.nextProtoNeg - serverHasALPN := len(hs.serverHello.alpnProtocol) > 0 + serverHasNPN := hs.serverHello.extensions.nextProtoNeg + serverHasALPN := len(hs.serverHello.extensions.alpnProtocol) > 0 if !clientDidNPN && serverHasNPN { c.sendAlert(alertHandshakeFailure) @@ -720,24 +720,24 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { } if serverHasALPN { - c.clientProtocol = hs.serverHello.alpnProtocol + c.clientProtocol = hs.serverHello.extensions.alpnProtocol c.clientProtocolFallback = false c.usedALPN = true } - if !hs.hello.channelIDSupported && hs.serverHello.channelIDRequested { + if !hs.hello.channelIDSupported && hs.serverHello.extensions.channelIDRequested { c.sendAlert(alertHandshakeFailure) return false, errors.New("server advertised unrequested Channel ID extension") } - if hs.serverHello.srtpProtectionProfile != 0 { - if hs.serverHello.srtpMasterKeyIdentifier != "" { + if hs.serverHello.extensions.srtpProtectionProfile != 0 { + if hs.serverHello.extensions.srtpMasterKeyIdentifier != "" { return false, errors.New("tls: server selected SRTP MKI value") } found := false for _, p := range c.config.SRTPProtectionProfiles { - if p == hs.serverHello.srtpProtectionProfile { + if p == hs.serverHello.extensions.srtpProtectionProfile { found = true break } @@ -746,7 +746,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return false, errors.New("tls: server advertised unsupported SRTP profile") } - c.srtpProtectionProfile = hs.serverHello.srtpProtectionProfile + c.srtpProtectionProfile = hs.serverHello.extensions.srtpProtectionProfile } if hs.serverResumedSession() { @@ -756,11 +756,11 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return false, errors.New("tls: server resumed session on renegotiation") } - if hs.serverHello.sctList != nil { + if hs.serverHello.extensions.sctList != nil { return false, errors.New("tls: server sent SCT extension on session resumption") } - if hs.serverHello.ocspStapling { + if hs.serverHello.extensions.ocspStapling { return false, errors.New("tls: server sent OCSP extension on session resumption") } @@ -774,8 +774,8 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return true, nil } - if hs.serverHello.sctList != nil { - c.sctList = hs.serverHello.sctList + if hs.serverHello.extensions.sctList != nil { + c.sctList = hs.serverHello.extensions.sctList } return false, nil @@ -828,7 +828,7 @@ func (hs *clientHandshakeState) readSessionTicket() error { ocspResponse: c.ocspResponse, } - if !hs.serverHello.ticketSupported { + if !hs.serverHello.extensions.ticketSupported { if c.config.Bugs.ExpectNewTicket { return errors.New("tls: expected new ticket") } @@ -866,9 +866,9 @@ func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error { var postCCSBytes []byte seqno := hs.c.sendHandshakeSeq - if hs.serverHello.nextProtoNeg { + if hs.serverHello.extensions.nextProtoNeg { nextProto := new(nextProtoMsg) - proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos) + proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.extensions.nextProtos) nextProto.proto = proto c.clientProtocol = proto c.clientProtocolFallback = fallback @@ -879,7 +879,7 @@ func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error { postCCSBytes = append(postCCSBytes, nextProtoBytes...) } - if hs.serverHello.channelIDRequested { + if hs.serverHello.extensions.channelIDRequested { channelIDMsg := new(channelIDMsg) if c.config.ChannelID.Curve != elliptic.P256() { return fmt.Errorf("tls: Channel ID is not on P-256.") diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index b4f0c5b54..1f678fadf 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -575,28 +575,14 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } type serverHelloMsg struct { - raw []byte - isDTLS bool - vers uint16 - random []byte - sessionId []byte - cipherSuite uint16 - compressionMethod uint8 - nextProtoNeg bool - nextProtos []string - ocspStapling bool - ticketSupported bool - secureRenegotiation []byte - alpnProtocol string - alpnProtocolEmpty bool - duplicateExtension bool - channelIDRequested bool - extendedMasterSecret bool - srtpProtectionProfile uint16 - srtpMasterKeyIdentifier string - sctList []byte - customExtension string - npnLast bool + raw []byte + isDTLS bool + vers uint16 + random []byte + sessionId []byte + cipherSuite uint16 + compressionMethod uint8 + extensions serverExtensions } func (m *serverHelloMsg) marshal() []byte { @@ -616,6 +602,78 @@ func (m *serverHelloMsg) marshal() []byte { hello.addU8(m.compressionMethod) extensions := hello.addU16LengthPrefixed() + + m.extensions.marshal(extensions) + + if extensions.len() == 0 { + hello.discardChild() + } + + m.raw = handshakeMsg.finish() + return m.raw +} + +func (m *serverHelloMsg) unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.raw = data + m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS) + m.random = data[6:38] + sessionIdLen := int(data[38]) + if sessionIdLen > 32 || len(data) < 39+sessionIdLen { + return false + } + m.sessionId = data[39 : 39+sessionIdLen] + data = data[39+sessionIdLen:] + if len(data) < 3 { + return false + } + m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) + m.compressionMethod = data[2] + data = data[3:] + + if len(data) == 0 { + // ServerHello is optionally followed by extension data + m.extensions = serverExtensions{} + return true + } + if len(data) < 2 { + return false + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if len(data) != extensionsLength { + return false + } + + if !m.extensions.unmarshal(data) { + return false + } + + return true +} + +type serverExtensions struct { + nextProtoNeg bool + nextProtos []string + ocspStapling bool + ticketSupported bool + secureRenegotiation []byte + alpnProtocol string + alpnProtocolEmpty bool + duplicateExtension bool + channelIDRequested bool + extendedMasterSecret bool + srtpProtectionProfile uint16 + srtpMasterKeyIdentifier string + sctList []byte + customExtension string + npnLast bool +} + +func (m *serverExtensions) marshal(extensions *byteBuilder) { if m.duplicateExtension { // Add a duplicate bogus extension at the beginning and end. extensions.addU16(0xffff) @@ -700,57 +758,11 @@ func (m *serverHelloMsg) marshal() []byte { npn.addBytes([]byte(v)) } } - - if extensions.len() == 0 { - hello.discardChild() - } - - m.raw = handshakeMsg.finish() - return m.raw } -func (m *serverHelloMsg) unmarshal(data []byte) bool { - if len(data) < 42 { - return false - } - m.raw = data - m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS) - m.random = data[6:38] - sessionIdLen := int(data[38]) - if sessionIdLen > 32 || len(data) < 39+sessionIdLen { - return false - } - m.sessionId = data[39 : 39+sessionIdLen] - data = data[39+sessionIdLen:] - if len(data) < 3 { - return false - } - m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) - m.compressionMethod = data[2] - data = data[3:] - - m.nextProtoNeg = false - m.nextProtos = nil - m.ocspStapling = false - m.ticketSupported = false - m.alpnProtocol = "" - m.alpnProtocolEmpty = false - m.extendedMasterSecret = false - m.customExtension = "" - - if len(data) == 0 { - // ServerHello is optionally followed by extension data - return true - } - if len(data) < 2 { - return false - } - - extensionsLength := int(data[0])<<8 | int(data[1]) - data = data[2:] - if len(data) != extensionsLength { - return false - } +func (m *serverExtensions) unmarshal(data []byte) bool { + // Reset all fields. + *m = serverExtensions{} for len(data) != 0 { if len(data) < 4 { diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 2d9db44d1..94f5923e3 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -219,9 +219,11 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { c.haveVers = true hs.hello = &serverHelloMsg{ - isDTLS: c.isDTLS, - customExtension: config.Bugs.CustomExtension, - npnLast: config.Bugs.SwapNPNAndALPN, + isDTLS: c.isDTLS, + extensions: serverExtensions{ + customExtension: config.Bugs.CustomExtension, + npnLast: config.Bugs.SwapNPNAndALPN, + }, } supportedCurve := false @@ -276,33 +278,33 @@ Curves: } if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo { - hs.hello.secureRenegotiation = append(hs.hello.secureRenegotiation, c.clientVerify...) - hs.hello.secureRenegotiation = append(hs.hello.secureRenegotiation, c.serverVerify...) + hs.hello.extensions.secureRenegotiation = append(hs.hello.extensions.secureRenegotiation, c.clientVerify...) + hs.hello.extensions.secureRenegotiation = append(hs.hello.extensions.secureRenegotiation, c.serverVerify...) if c.config.Bugs.BadRenegotiationInfo { - hs.hello.secureRenegotiation[0] ^= 0x80 + hs.hello.extensions.secureRenegotiation[0] ^= 0x80 } } else { - hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation + hs.hello.extensions.secureRenegotiation = hs.clientHello.secureRenegotiation } if c.noRenegotiationInfo() { - hs.hello.secureRenegotiation = nil + hs.hello.extensions.secureRenegotiation = nil } hs.hello.compressionMethod = compressionNone - hs.hello.duplicateExtension = c.config.Bugs.DuplicateExtension + hs.hello.extensions.duplicateExtension = c.config.Bugs.DuplicateExtension if len(hs.clientHello.serverName) > 0 { c.serverName = hs.clientHello.serverName } if len(hs.clientHello.alpnProtocols) > 0 { if proto := c.config.Bugs.ALPNProtocol; proto != nil { - hs.hello.alpnProtocol = *proto - hs.hello.alpnProtocolEmpty = len(*proto) == 0 + hs.hello.extensions.alpnProtocol = *proto + hs.hello.extensions.alpnProtocolEmpty = len(*proto) == 0 c.clientProtocol = *proto c.usedALPN = true } else if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { - hs.hello.alpnProtocol = selectedProto + hs.hello.extensions.alpnProtocol = selectedProto c.clientProtocol = selectedProto c.usedALPN = true } @@ -313,11 +315,11 @@ Curves: // config.NextProtos is empty. See // https://code.google.com/p/go/issues/detail?id=5445. if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 { - hs.hello.nextProtoNeg = true - hs.hello.nextProtos = config.NextProtos + hs.hello.extensions.nextProtoNeg = true + hs.hello.extensions.nextProtos = config.NextProtos } } - hs.hello.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret + hs.hello.extensions.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret if len(config.Certificates) == 0 { c.sendAlert(alertInternalError) @@ -332,7 +334,7 @@ Curves: } if hs.clientHello.channelIDSupported && config.RequestChannelID { - hs.hello.channelIDRequested = true + hs.hello.extensions.channelIDRequested = true } if hs.clientHello.srtpProtectionProfiles != nil { @@ -340,7 +342,7 @@ Curves: for _, p1 := range c.config.SRTPProtectionProfiles { for _, p2 := range hs.clientHello.srtpProtectionProfiles { if p1 == p2 { - hs.hello.srtpProtectionProfile = p1 + hs.hello.extensions.srtpProtectionProfile = p1 c.srtpProtectionProfile = p1 break SRTPLoop } @@ -349,7 +351,7 @@ Curves: } if c.config.Bugs.SendSRTPProtectionProfile != 0 { - hs.hello.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile + hs.hello.extensions.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile } if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil { @@ -485,10 +487,10 @@ func (hs *serverHandshakeState) doResumeHandshake() error { // We echo the client's session ID in the ServerHello to let it know // that we're doing a resumption. hs.hello.sessionId = hs.clientHello.sessionId - hs.hello.ticketSupported = c.config.Bugs.RenewTicketOnResume + hs.hello.extensions.ticketSupported = c.config.Bugs.RenewTicketOnResume if c.config.Bugs.SendSCTListOnResume != nil { - hs.hello.sctList = c.config.Bugs.SendSCTListOnResume + hs.hello.extensions.sctList = c.config.Bugs.SendSCTListOnResume } hs.finishedHash = newFinishedHash(c.vers, hs.suite) @@ -516,22 +518,22 @@ func (hs *serverHandshakeState) doFullHandshake() error { isPSK := hs.suite.flags&suitePSK != 0 if !isPSK && hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { - hs.hello.ocspStapling = true + hs.hello.extensions.ocspStapling = true } if hs.clientHello.sctListSupported && len(hs.cert.SignedCertificateTimestampList) > 0 { - hs.hello.sctList = hs.cert.SignedCertificateTimestampList + hs.hello.extensions.sctList = hs.cert.SignedCertificateTimestampList } - hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled && c.vers > VersionSSL30 + hs.hello.extensions.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled && c.vers > VersionSSL30 hs.hello.cipherSuite = hs.suite.id if config.Bugs.SendCipherSuite != 0 { hs.hello.cipherSuite = config.Bugs.SendCipherSuite } - c.extendedMasterSecret = hs.hello.extendedMasterSecret + c.extendedMasterSecret = hs.hello.extensions.extendedMasterSecret // Generate a session ID if we're to save the session. - if !hs.hello.ticketSupported && config.ServerSessionCache != nil { + if !hs.hello.extensions.ticketSupported && config.ServerSessionCache != nil { hs.hello.sessionId = make([]byte, 32) if _, err := io.ReadFull(config.rand(), hs.hello.sessionId); err != nil { c.sendAlert(alertInternalError) @@ -560,7 +562,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { } } - if hs.hello.ocspStapling && !c.config.Bugs.SkipCertificateStatus { + if hs.hello.extensions.ocspStapling && !c.config.Bugs.SkipCertificateStatus { certStatus := new(certificateStatusMsg) certStatus.statusType = statusTypeOCSP certStatus.response = hs.cert.OCSPStaple @@ -774,7 +776,7 @@ func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error { return err } - if hs.hello.nextProtoNeg { + if hs.hello.extensions.nextProtoNeg { msg, err := c.readHandshake() if err != nil { return err @@ -788,7 +790,7 @@ func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error { c.clientProtocol = nextProto.proto } - if hs.hello.channelIDRequested { + if hs.hello.extensions.channelIDRequested { msg, err := c.readHandshake() if err != nil { return err @@ -851,7 +853,7 @@ func (hs *serverHandshakeState) sendSessionTicket() error { handshakeHash: hs.finishedHash.server.Sum(nil), } - if !hs.hello.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket { + if !hs.hello.extensions.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket { if c.config.ServerSessionCache != nil && len(hs.hello.sessionId) != 0 { c.config.ServerSessionCache.Put(string(hs.hello.sessionId), &state) } From e5d577d70ee243450eb3317165667edf4dcfc59f Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Fri, 1 Jul 2016 14:18:29 -0400 Subject: [PATCH 24/40] Add Go HKDF implementation with test. [Originally written by nharper. Test added by davidben.] Test vectors taken from hkdf_test.c. Change-Id: I214bcae325e9c7c242632a169ab5cf80a3178989 Reviewed-on: https://boringssl-review.googlesource.com/8587 Reviewed-by: David Benjamin --- ssl/test/runner/hkdf.go | 57 ++++++++++++++++++++ ssl/test/runner/hkdf_test.go | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 ssl/test/runner/hkdf.go create mode 100644 ssl/test/runner/hkdf_test.go diff --git a/ssl/test/runner/hkdf.go b/ssl/test/runner/hkdf.go new file mode 100644 index 000000000..5c10350a1 --- /dev/null +++ b/ssl/test/runner/hkdf.go @@ -0,0 +1,57 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +package runner + +import ( + "crypto/hmac" + "hash" +) + +// hkdfExtract implements HKDF-Extract from RFC 5869. +func hkdfExtract(hash func() hash.Hash, salt, ikm []byte) []byte { + if salt == nil { + salt = make([]byte, hash().Size()) + } + hmac := hmac.New(hash, salt) + hmac.Write(ikm) + return hmac.Sum(nil) +} + +// hkdfExpand implements HKDF-Expand from RFC 5869. +func hkdfExpand(hash func() hash.Hash, prk, info []byte, length int) []byte { + hashSize := hash().Size() + if length > 255*hashSize { + panic("hkdfExpand: length too long") + } + if len(prk) < hashSize { + panic("hkdfExpand: prk too short") + } + var lastBlock []byte + counter := byte(0) + okm := make([]byte, length) + hmac := hmac.New(hash, prk) + for length > 0 { + hmac.Reset() + counter++ + hmac.Write(lastBlock) + hmac.Write(info) + hmac.Write([]byte{counter}) + block := hmac.Sum(nil) + lastBlock = block + copy(okm[(int(counter)-1)*hashSize:], block) + length -= hashSize + } + return okm +} diff --git a/ssl/test/runner/hkdf_test.go b/ssl/test/runner/hkdf_test.go new file mode 100644 index 000000000..33031ff5a --- /dev/null +++ b/ssl/test/runner/hkdf_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +package runner + +import ( + "bytes" + "crypto" + _ "crypto/sha1" + _ "crypto/sha256" + "testing" +) + +var hkdfTests = []struct { + hash crypto.Hash + ikm, salt, info, prk, okm []byte +}{ + { + crypto.SHA256, + []byte{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}, + []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}, + []byte{0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}, + []byte{0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}, + }, + { + crypto.SHA256, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}, + []byte{0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf}, + []byte{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}, + []byte{0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44}, + []byte{0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87}, + }, + { + crypto.SHA256, + []byte{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{}, + []byte{}, + []byte{0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04}, + []byte{0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8}, + }, + { + crypto.SHA1, + []byte{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}, + []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}, + []byte{0x9b, 0x6c, 0x18, 0xc4, 0x32, 0xa7, 0xbf, 0x8f, 0x0e, 0x71, 0xc8, 0xeb, 0x88, 0xf4, 0xb3, 0x0b, 0xaa, 0x2b, 0xa2, 0x43}, + []byte{0x08, 0x5a, 0x01, 0xea, 0x1b, 0x10, 0xf3, 0x69, 0x33, 0x06, 0x8b, 0x56, 0xef, 0xa5, 0xad, 0x81, 0xa4, 0xf1, 0x4b, 0x82, 0x2f, 0x5b, 0x09, 0x15, 0x68, 0xa9, 0xcd, 0xd4, 0xf1, 0x55, 0xfd, 0xa2, 0xc2, 0x2e, 0x42, 0x24, 0x78, 0xd3, 0x05, 0xf3, 0xf8, 0x96}, + }, + { + crypto.SHA1, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}, + []byte{0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf}, + []byte{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}, + []byte{0x8a, 0xda, 0xe0, 0x9a, 0x2a, 0x30, 0x70, 0x59, 0x47, 0x8d, 0x30, 0x9b, 0x26, 0xc4, 0x11, 0x5a, 0x22, 0x4c, 0xfa, 0xf6}, + []byte{0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1, 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d, 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, 0x2f, 0xfe, 0x8f, 0xa3, 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2, 0x17, 0x3c, 0x48, 0x6e, 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c, 0x7f, 0x9d, 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f, 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, 0xd3, 0xb4}, + }, + { + crypto.SHA1, + []byte{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{}, + []byte{}, + []byte{0xda, 0x8c, 0x8a, 0x73, 0xc7, 0xfa, 0x77, 0x28, 0x8e, 0xc6, 0xf5, 0xe7, 0xc2, 0x97, 0x78, 0x6a, 0xa0, 0xd3, 0x2d, 0x01}, + []byte{0x0a, 0xc1, 0xaf, 0x70, 0x02, 0xb3, 0xd7, 0x61, 0xd1, 0xe5, 0x52, 0x98, 0xda, 0x9d, 0x05, 0x06, 0xb9, 0xae, 0x52, 0x05, 0x72, 0x20, 0xa3, 0x06, 0xe0, 0x7b, 0x6b, 0x87, 0xe8, 0xdf, 0x21, 0xd0, 0xea, 0x00, 0x03, 0x3d, 0xe0, 0x39, 0x84, 0xd3, 0x49, 0x18}, + }, + { + crypto.SHA1, + []byte{0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c}, + []byte{}, + []byte{}, + []byte{0x2a, 0xdc, 0xca, 0xda, 0x18, 0x77, 0x9e, 0x7c, 0x20, 0x77, 0xad, 0x2e, 0xb1, 0x9d, 0x3f, 0x3e, 0x73, 0x13, 0x85, 0xdd}, + []byte{0x2c, 0x91, 0x11, 0x72, 0x04, 0xd7, 0x45, 0xf3, 0x50, 0x0d, 0x63, 0x6a, 0x62, 0xf6, 0x4f, 0x0a, 0xb3, 0xba, 0xe5, 0x48, 0xaa, 0x53, 0xd4, 0x23, 0xb0, 0xd1, 0xf2, 0x7e, 0xbb, 0xa6, 0xf5, 0xe5, 0x67, 0x3a, 0x08, 0x1d, 0x70, 0xcc, 0xe7, 0xac, 0xfc, 0x48}, + }, +} + +func TestHKDF(t *testing.T) { + for i, tt := range hkdfTests { + prk := hkdfExtract(tt.hash.New, tt.salt, tt.ikm) + if !bytes.Equal(prk, tt.prk) { + t.Errorf("%d. got hkdfExtract(%x, %x) = %x; wanted %x", i+1, tt.salt, tt.ikm, prk, tt.prk) + } + + okm := hkdfExpand(tt.hash.New, tt.prk, tt.info, len(tt.okm)) + if !bytes.Equal(okm, tt.okm) { + t.Errorf("%d. got hkdfExpand(%x, %x, %d) = %x; wanted %x", i+1, tt.prk, tt.info, len(tt.okm), okm, tt.okm) + } + } +} From 7e0442a217cebe654e7771e5048daf4c3dffe792 Mon Sep 17 00:00:00 2001 From: Nick Harper Date: Fri, 1 Jul 2016 17:40:09 -0400 Subject: [PATCH 25/40] Rewrite Go Certificate and CertificateRequest serialization. [Originally written by nharper and then tweaked by davidben.] TLS 1.3 tweaks them slightly, so being able to write them in one pass rather than two will be somewhat more convenient. Change-Id: Ib7e2d63e28cbae025c840bbb34e9e9c295b44dc6 Reviewed-on: https://boringssl-review.googlesource.com/8588 Reviewed-by: David Benjamin --- ssl/test/runner/handshake_messages.go | 88 +++++++-------------------- 1 file changed, 23 insertions(+), 65 deletions(-) diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 1f678fadf..23b5c8ea9 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -865,34 +865,17 @@ func (m *certificateMsg) marshal() (x []byte) { return m.raw } - var i int - for _, slice := range m.certificates { - i += len(slice) + certMsg := newByteBuilder() + certMsg.addU8(typeCertificate) + certificate := certMsg.addU24LengthPrefixed() + certificateList := certificate.addU24LengthPrefixed() + for _, cert := range m.certificates { + certEntry := certificateList.addU24LengthPrefixed() + certEntry.addBytes(cert) } - length := 3 + 3*len(m.certificates) + i - x = make([]byte, 4+length) - x[0] = typeCertificate - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - certificateOctets := length - 3 - x[4] = uint8(certificateOctets >> 16) - x[5] = uint8(certificateOctets >> 8) - x[6] = uint8(certificateOctets) - - y := x[7:] - for _, slice := range m.certificates { - y[0] = uint8(len(slice) >> 16) - y[1] = uint8(len(slice) >> 8) - y[2] = uint8(len(slice)) - copy(y[3:], slice) - y = y[3+len(slice):] - } - - m.raw = x - return + m.raw = certMsg.finish() + return m.raw } func (m *certificateMsg) unmarshal(data []byte) bool { @@ -1162,59 +1145,34 @@ type certificateRequestMsg struct { certificateAuthorities [][]byte } -func (m *certificateRequestMsg) marshal() (x []byte) { +func (m *certificateRequestMsg) marshal() []byte { if m.raw != nil { return m.raw } // See http://tools.ietf.org/html/rfc4346#section-7.4.4 - length := 1 + len(m.certificateTypes) + 2 - casLength := 0 - for _, ca := range m.certificateAuthorities { - casLength += 2 + len(ca) - } - length += casLength + builder := newByteBuilder() + builder.addU8(typeCertificateRequest) + body := builder.addU24LengthPrefixed() + + certificateTypes := body.addU8LengthPrefixed() + certificateTypes.addBytes(m.certificateTypes) if m.hasSignatureAlgorithm { - length += 2 + 2*len(m.signatureAlgorithms) - } - - x = make([]byte, 4+length) - x[0] = typeCertificateRequest - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - x[4] = uint8(len(m.certificateTypes)) - - copy(x[5:], m.certificateTypes) - y := x[5+len(m.certificateTypes):] - - if m.hasSignatureAlgorithm { - n := len(m.signatureAlgorithms) * 2 - y[0] = uint8(n >> 8) - y[1] = uint8(n) - y = y[2:] + signatureAlgorithms := body.addU16LengthPrefixed() for _, sigAlg := range m.signatureAlgorithms { - y[0] = byte(sigAlg >> 8) - y[1] = byte(sigAlg) - y = y[2:] + signatureAlgorithms.addU16(uint16(sigAlg)) } } - y[0] = uint8(casLength >> 8) - y[1] = uint8(casLength) - y = y[2:] + certificateAuthorities := body.addU16LengthPrefixed() for _, ca := range m.certificateAuthorities { - y[0] = uint8(len(ca) >> 8) - y[1] = uint8(len(ca)) - y = y[2:] - copy(y, ca) - y = y[len(ca):] + caEntry := certificateAuthorities.addU16LengthPrefixed() + caEntry.addBytes(ca) } - m.raw = x - return + m.raw = builder.finish() + return m.raw } func (m *certificateRequestMsg) unmarshal(data []byte) bool { From a6f82637da8a2873c4c9670741f02647bb0a9f7a Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 1 Jul 2016 18:44:02 -0400 Subject: [PATCH 26/40] Extract Go CertificateRequest logic into a helper. TLS 1.3 will need to call it under different circumstances. We will also wish to test TLS 1.3 post-handshake auth, so this function must work without being passed handshake state. In doing so, implement matching based on signature algorithms as 1.3 does away with the certificate type list. Change-Id: Ibdee44bbbb589686fcbcd7412432100279bfac63 Reviewed-on: https://boringssl-review.googlesource.com/8589 Reviewed-by: David Benjamin --- ssl/test/runner/handshake_client.go | 143 ++++++++++++++++------------ 1 file changed, 81 insertions(+), 62 deletions(-) diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index d3644f5d9..3fb8f951d 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -488,70 +488,11 @@ func (hs *clientHandshakeState) doFullHandshake() error { if ok { certRequested = true - // RFC 4346 on the certificateAuthorities field: - // A list of the distinguished names of acceptable certificate - // authorities. These distinguished names may specify a desired - // distinguished name for a root CA or for a subordinate CA; - // thus, this message can be used to describe both known roots - // and a desired authorization space. If the - // certificate_authorities list is empty then the client MAY - // send any certificate of the appropriate - // ClientCertificateType, unless there is some external - // arrangement to the contrary. - hs.writeServerHash(certReq.marshal()) - var rsaAvail, ecdsaAvail bool - for _, certType := range certReq.certificateTypes { - switch certType { - case CertTypeRSASign: - rsaAvail = true - case CertTypeECDSASign: - ecdsaAvail = true - } - } - - // We need to search our list of client certs for one - // where SignatureAlgorithm is RSA and the Issuer is in - // certReq.certificateAuthorities - findCert: - for i, chain := range c.config.Certificates { - if !rsaAvail && !ecdsaAvail { - continue - } - - for j, cert := range chain.Certificate { - x509Cert := chain.Leaf - // parse the certificate if this isn't the leaf - // node, or if chain.Leaf was nil - if j != 0 || x509Cert == nil { - if x509Cert, err = x509.ParseCertificate(cert); err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) - } - } - - switch { - case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: - case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: - default: - continue findCert - } - - if len(certReq.certificateAuthorities) == 0 { - // they gave us an empty list, so just take the - // first RSA cert from c.config.Certificates - chainToSend = &chain - break findCert - } - - for _, ca := range certReq.certificateAuthorities { - if bytes.Equal(x509Cert.RawIssuer, ca) { - chainToSend = &chain - break findCert - } - } - } + chainToSend, err = selectClientCertificate(c, certReq) + if err != nil { + return err } msg, err = c.readHandshake() @@ -978,6 +919,84 @@ func (hs *clientHandshakeState) writeHash(msg []byte, seqno uint16) { } } +// selectClientCertificate selects a certificate for use with the given +// certificate, or none if none match. It may return a particular certificate or +// nil on success, or an error on internal error. +func selectClientCertificate(c *Conn, certReq *certificateRequestMsg) (*Certificate, error) { + // RFC 4346 on the certificateAuthorities field: + // A list of the distinguished names of acceptable certificate + // authorities. These distinguished names may specify a desired + // distinguished name for a root CA or for a subordinate CA; thus, this + // message can be used to describe both known roots and a desired + // authorization space. If the certificate_authorities list is empty + // then the client MAY send any certificate of the appropriate + // ClientCertificateType, unless there is some external arrangement to + // the contrary. + + var rsaAvail, ecdsaAvail bool + for _, certType := range certReq.certificateTypes { + switch certType { + case CertTypeRSASign: + rsaAvail = true + case CertTypeECDSASign: + ecdsaAvail = true + } + } + + // We need to search our list of client certs for one + // where SignatureAlgorithm is RSA and the Issuer is in + // certReq.certificateAuthorities +findCert: + for i, chain := range c.config.Certificates { + if !rsaAvail && !ecdsaAvail { + continue + } + + // Ensure the private key supports one of the advertised + // signature algorithms. + if certReq.hasSignatureAlgorithm { + if _, err := selectSignatureAlgorithm(c.vers, chain.PrivateKey, certReq.signatureAlgorithms, c.config.signatureAlgorithmsForClient()); err != nil { + continue + } + } + + for j, cert := range chain.Certificate { + x509Cert := chain.Leaf + // parse the certificate if this isn't the leaf + // node, or if chain.Leaf was nil + if j != 0 || x509Cert == nil { + var err error + if x509Cert, err = x509.ParseCertificate(cert); err != nil { + c.sendAlert(alertInternalError) + return nil, errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } + } + + switch { + case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: + case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: + default: + continue findCert + } + + if len(certReq.certificateAuthorities) == 0 { + // They gave us an empty list, so just take the + // first certificate of valid type from + // c.config.Certificates. + return &chain, nil + } + + for _, ca := range certReq.certificateAuthorities { + if bytes.Equal(x509Cert.RawIssuer, ca) { + return &chain, nil + } + } + } + } + + return nil, nil +} + // clientSessionCacheKey returns a key used to cache sessionTickets that could // be used to resume previously negotiated TLS sessions with a server. func clientSessionCacheKey(serverAddr net.Addr, config *Config) string { From 75051445588f3fa3f6a5ecffaf50b56300f7a105 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 1 Jul 2016 18:58:51 -0400 Subject: [PATCH 27/40] Extract certificate message processing in Go. TLS 1.2 and 1.3 will both need to call it at different points. Change-Id: Id62ec289213aa6c06ebe5fe65a57ca6c2b53d538 Reviewed-on: https://boringssl-review.googlesource.com/8600 Reviewed-by: David Benjamin --- ssl/test/runner/handshake_client.go | 97 +++++++++++++++++------------ 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 3fb8f951d..8feb59f3c 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -392,53 +392,16 @@ func (hs *clientHandshakeState) doFullHandshake() error { } certMsg, ok := msg.(*certificateMsg) - if !ok || len(certMsg.certificates) == 0 { + if !ok { c.sendAlert(alertUnexpectedMessage) return unexpectedMessageError(certMsg, msg) } hs.writeServerHash(certMsg.marshal()) - certs := make([]*x509.Certificate, len(certMsg.certificates)) - for i, asn1Data := range certMsg.certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse certificate from server: " + err.Error()) - } - certs[i] = cert + if err := hs.verifyCertificates(certMsg); err != nil { + return err } - leaf = certs[0] - - if !c.config.InsecureSkipVerify { - opts := x509.VerifyOptions{ - Roots: c.config.RootCAs, - CurrentTime: c.config.time(), - DNSName: c.config.ServerName, - Intermediates: x509.NewCertPool(), - } - - for i, cert := range certs { - if i == 0 { - continue - } - opts.Intermediates.AddCert(cert) - } - c.verifiedChains, err = leaf.Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - switch leaf.PublicKey.(type) { - case *rsa.PublicKey, *ecdsa.PublicKey: - break - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", leaf.PublicKey) - } - - c.peerCertificates = certs + leaf = c.peerCertificates[0] } if hs.serverHello.extensions.ocspStapling { @@ -603,6 +566,58 @@ func (hs *clientHandshakeState) doFullHandshake() error { return nil } +func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) error { + c := hs.c + + if len(certMsg.certificates) == 0 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: no certificates sent") + } + + certs := make([]*x509.Certificate, len(certMsg.certificates)) + for i, asn1Data := range certMsg.certificates { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse certificate from server: " + err.Error()) + } + certs[i] = cert + } + + if !c.config.InsecureSkipVerify { + opts := x509.VerifyOptions{ + Roots: c.config.RootCAs, + CurrentTime: c.config.time(), + DNSName: c.config.ServerName, + Intermediates: x509.NewCertPool(), + } + + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + var err error + c.verifiedChains, err = certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + switch certs[0].PublicKey.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey: + break + default: + c.sendAlert(alertUnsupportedCertificate) + return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) + } + + c.peerCertificates = certs + return nil +} + func (hs *clientHandshakeState) establishKeys() error { c := hs.c From 0186787c49ea449b7cb0cd74d02d00de5eb997a6 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Thu, 30 Jun 2016 14:16:59 -0700 Subject: [PATCH 28/40] Add top-level BUILD file (in util/). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we have *-with-bazel branches this BUILD file will be copied to the top-level for consumers that want to use Bazel. From empirical testing, x86-64 on Linux is spelt “k8” and x86-64 on macOS is spelt “darwin”. I've not tried to enable assembly for any other cases yet. Change-Id: Ic6cb739565f145db20756fb57c0d087227fd9e18 Reviewed-on: https://boringssl-review.googlesource.com/8571 Reviewed-by: Adam Langley --- CMakeLists.txt | 4 +- util/BUILD | 128 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 util/BUILD diff --git a/CMakeLists.txt b/CMakeLists.txt index f85b43912..050ba9747 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,8 +31,8 @@ if (NOT GO_EXECUTABLE) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(C_CXX_FLAGS "-Wall -Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -ggdb -fvisibility=hidden") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_CXX_FLAGS} -Wmissing-prototypes") + set(C_CXX_FLAGS "-Wall -Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -Wwrite-strings -ggdb -fvisibility=hidden -fno-common") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_CXX_FLAGS} -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${C_CXX_FLAGS} -Wmissing-declarations") elseif(MSVC) set(MSVC_DISABLED_WARNINGS_LIST diff --git a/util/BUILD b/util/BUILD new file mode 100644 index 000000000..86bcc786c --- /dev/null +++ b/util/BUILD @@ -0,0 +1,128 @@ +# Copyright (c) 2016, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +licenses(["notice"]) + +exports_files(["LICENSE"]) + +load( + ":BUILD.generated.bzl", + "crypto_headers", + "crypto_internal_headers", + "crypto_sources", + "crypto_sources_linux_x86_64", + "crypto_sources_mac_x86_64", + "ssl_headers", + "ssl_internal_headers", + "ssl_sources", + "tool_sources", + "tool_headers", +) + +config_setting( + name = "linux_x86_64", + values = {"cpu": "k8"}, +) + +config_setting( + name = "mac_x86_64", + values = {"cpu": "darwin"}, +) + +boringssl_copts = [ + # Assembler option --noexecstack adds .note.GNU-stack to each object to + # ensure that binaries can be built with non-executable stack. + "-Wa,--noexecstack", + + # This is needed on Linux systems (at least) to get rwlock in pthread. + "-D_XOPEN_SOURCE=700", + + # Exported symbols are annotated specifically in header files. This option + # sets the default for all other symbols to be hidden. (Applies only to + # shared-library builds.) + "-fvisibility=hidden", + + # This list of warnings should match those in the top-level CMakeLists.txt. + "-Wall", + "-Werror", + "-Wformat=2", + "-Wsign-compare", + "-Wmissing-field-initializers", + "-Wwrite-strings", + "-Wshadow", + "-fno-common", + + # Modern build environments should be able to set this to use atomic + # operations for reference counting rather than locks. However, it's + # known not to work on some Android builds. + # "-DOPENSSL_C11_ATOMIC", +] + select({ + ":linux_x86_64": [], + ":mac_x86_64": [], + "//conditions:default": ["-DOPENSSL_NO_ASM"], +}) + +crypto_sources_asm = select({ + ":linux_x86_64": crypto_sources_linux_x86_64, + ":mac_x86_64": crypto_sources_mac_x86_64, + "//conditions:default": [], +}) + +# For C targets only (not C++), compile with C11 support. +boringssl_copts_c11 = boringssl_copts + [ + "-std=c11", + "-Wmissing-prototypes", + "-Wold-style-definition", + "-Wstrict-prototypes", +] + +# For C targets only (not C++), compile with C11 support. +boringssl_copts_cxx = boringssl_copts + [ + "-std=c++11", + "-Wmissing-declarations", +] + +cc_library( + name = "crypto", + srcs = crypto_sources + crypto_internal_headers + crypto_sources_asm, + hdrs = crypto_headers, + copts = boringssl_copts_c11, + includes = ["src/include"], + linkopts = select({ + ":mac_x86_64": [], + "//conditions:default": ["-lpthread"], + }), + visibility = ["//visibility:public"], +) + +cc_library( + name = "ssl", + srcs = ssl_sources + ssl_internal_headers, + hdrs = ssl_headers, + copts = boringssl_copts_c11, + includes = ["src/include"], + visibility = ["//visibility:public"], + deps = [":crypto"], +) + +cc_binary( + name = "bssl", + srcs = tool_sources + tool_headers + [ + "src/crypto/test/scoped_types.h", + "src/ssl/test/scoped_types.h", + ], + copts = boringssl_copts_cxx, + visibility = ["//visibility:public"], + deps = [":ssl"], +) From 66a0e6e37d2841ae91af20aba3a3b85db07b6b62 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 13:35:49 -0400 Subject: [PATCH 29/40] Add a Go tool to check bn_tests.txt. Since the format no longer is readable by bc, compare it to Go's math/big instead. Change-Id: I34d37aa0c29c6f4178267858cb0d3941b4266b93 Reviewed-on: https://boringssl-review.googlesource.com/8603 Reviewed-by: David Benjamin --- crypto/bn/check_bn_tests.go | 221 ++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 crypto/bn/check_bn_tests.go diff --git a/crypto/bn/check_bn_tests.go b/crypto/bn/check_bn_tests.go new file mode 100644 index 000000000..61deee274 --- /dev/null +++ b/crypto/bn/check_bn_tests.go @@ -0,0 +1,221 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +package main + +import ( + "bufio" + "errors" + "fmt" + "io" + "math/big" + "os" + "strings" +) + +type test struct { + LineNumber int + Type string + Values map[string]*big.Int +} + +type testScanner struct { + scanner *bufio.Scanner + lineNo int + err error + test test +} + +func newTestScanner(r io.Reader) *testScanner { + return &testScanner{scanner: bufio.NewScanner(r)} +} + +func (s *testScanner) scanLine() bool { + if !s.scanner.Scan() { + return false + } + s.lineNo++ + return true +} + +func (s *testScanner) addAttribute(line string) (key string, ok bool) { + fields := strings.SplitN(line, "=", 2) + if len(fields) != 2 { + s.setError(errors.New("invalid syntax")) + return "", false + } + + key = strings.TrimSpace(fields[0]) + value := strings.TrimSpace(fields[1]) + + valueInt, ok := new(big.Int).SetString(value, 16) + if !ok { + s.setError(fmt.Errorf("could not parse %q", value)) + return "", false + } + if _, dup := s.test.Values[key]; dup { + s.setError(fmt.Errorf("duplicate key %q", key)) + return "", false + } + s.test.Values[key] = valueInt + return key, true +} + +func (s *testScanner) Scan() bool { + s.test = test{ + Values: make(map[string]*big.Int), + } + + // Scan until the first attribute. + for { + if !s.scanLine() { + return false + } + if len(s.scanner.Text()) != 0 && s.scanner.Text()[0] != '#' { + break + } + } + + var ok bool + s.test.Type, ok = s.addAttribute(s.scanner.Text()) + if !ok { + return false + } + s.test.LineNumber = s.lineNo + + for s.scanLine() { + if len(s.scanner.Text()) == 0 { + break + } + + if s.scanner.Text()[0] == '#' { + continue + } + + if _, ok := s.addAttribute(s.scanner.Text()); !ok { + return false + } + } + return s.scanner.Err() == nil +} + +func (s *testScanner) Test() test { + return s.test +} + +func (s *testScanner) Err() error { + if s.err != nil { + return s.err + } + return s.scanner.Err() +} + +func (s *testScanner) setError(err error) { + s.err = fmt.Errorf("line %d: %s", s.lineNo, err) +} + +func checkKeys(t test, keys ...string) bool { + var foundErrors bool + + for _, k := range keys { + if _, ok := t.Values[k]; !ok { + fmt.Fprintf(os.Stderr, "Line %d: missing key %q.\n", t.LineNumber, k) + foundErrors = true + } + } + + for k, _ := range t.Values { + var found bool + for _, k2 := range keys { + if k == k2 { + found = true + break + } + } + if !found { + fmt.Fprintf(os.Stderr, "Line %d: unexpected key %q.\n", t.LineNumber, k) + foundErrors = true + } + } + + return !foundErrors +} + +func checkResult(t test, expr, key string, r *big.Int) { + if t.Values[key].Cmp(r) != 0 { + fmt.Fprintf(os.Stderr, "Line %d: %s did not match %s.\n\tGot %s\n", t.LineNumber, expr, key, r.Text(16)) + } +} + +func main() { + if len(os.Args) != 2 { + fmt.Fprintf(os.Stderr, "Usage: %s bn_tests.txt\n", os.Args[0]) + os.Exit(1) + } + + in, err := os.Open(os.Args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "Error opening %s: %s.\n", os.Args[0], err) + os.Exit(1) + } + defer in.Close() + + scanner := newTestScanner(in) + for scanner.Scan() { + test := scanner.Test() + switch test.Type { + case "Sum": + if checkKeys(test, "A", "B", "Sum") { + r := new(big.Int).Add(test.Values["A"], test.Values["B"]) + checkResult(test, "A + B", "Sum", r) + } + case "LShift1": + if checkKeys(test, "A", "LShift1") { + r := new(big.Int).Add(test.Values["A"], test.Values["A"]) + checkResult(test, "A + A", "LShift1", r) + } + case "LShift": + if checkKeys(test, "A", "N", "LShift") { + r := new(big.Int).Lsh(test.Values["A"], uint(test.Values["N"].Uint64())) + checkResult(test, "A << N", "LShift", r) + } + case "RShift": + if checkKeys(test, "A", "N", "RShift") { + r := new(big.Int).Rsh(test.Values["A"], uint(test.Values["N"].Uint64())) + checkResult(test, "A >> N", "RShift", r) + } + case "Square": + if checkKeys(test, "A", "Square") { + r := new(big.Int).Mul(test.Values["A"], test.Values["A"]) + checkResult(test, "A * A", "Square", r) + } + case "Product": + if checkKeys(test, "A", "B", "Product") { + r := new(big.Int).Mul(test.Values["A"], test.Values["B"]) + checkResult(test, "A * B", "Product", r) + } + case "Quotient": + if checkKeys(test, "A", "B", "Quotient", "Remainder") { + q, r := new(big.Int).QuoRem(test.Values["A"], test.Values["B"], new(big.Int)) + checkResult(test, "A / B", "Quotient", q) + checkResult(test, "A % B", "Remainder", r) + } + default: + fmt.Fprintf(os.Stderr, "Line %d: unknown test type %q.\n", test.LineNumber, test.Type) + } + } + if scanner.Err() != nil { + fmt.Fprintf(os.Stderr, "Error reading tests: %s.\n", scanner.Err()) + } +} From 56cbbe5b8d9d38732aebf19da22132eac6a9ce47 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 14:24:30 -0400 Subject: [PATCH 30/40] Use BN_set_bit in TestLShift1. No need for the special case and such. Change-Id: If8fbc73eda0ccbaf3fd422e97c96fee6dc10b1ab Reviewed-on: https://boringssl-review.googlesource.com/8604 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index fe6e0b747..151164ee9 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -422,17 +422,8 @@ static bool TestLShift1(FileTest *t, BN_CTX *ctx) { } // Set the LSB to 1 and test rshift1 again. - if (BN_is_negative(lshift1.get())) { - if (!BN_sub(lshift1.get(), lshift1.get(), BN_value_one())) { - return false; - } - } else { - if (!BN_add(lshift1.get(), lshift1.get(), BN_value_one())) { - return false; - } - } - - if (!BN_div(ret.get(), nullptr /* rem */, lshift1.get(), two.get(), ctx) || + if (!BN_set_bit(lshift1.get(), 0) || + !BN_div(ret.get(), nullptr /* rem */, lshift1.get(), two.get(), ctx) || !ExpectBIGNUMsEqual(t, "(LShift1 | 1) / 2", a.get(), ret.get()) || !BN_rshift1(ret.get(), lshift1.get()) || !ExpectBIGNUMsEqual(t, "(LShift | 1) >> 1", a.get(), ret.get())) { From 7819409e132133b4196a7566c03fc4cb280e777a Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 14:25:43 -0400 Subject: [PATCH 31/40] Generate more test data in test_mont. In preparation for converting test_mont and test_mod_mul to test vectors, make test_mont less silly. We can certainly get away with doing more than five tests. Also generate |a| and |b| anew each time. Otherwise the first BN_nmod is destructive. Change-Id: I944007ed7b6013a16d972cb7290ab9992c9360ce Reviewed-on: https://boringssl-review.googlesource.com/8605 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 73 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 151164ee9..53de2069d 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -627,44 +627,43 @@ static bool test_mont(FILE *fp, BN_CTX *ctx) { return false; } - if (!BN_rand(a.get(), 100, 0, 0) || - !BN_rand(b.get(), 100, 0, 0)) { - return false; - } + for (int j = 0; j < 20; j++) { + for (int i = 0; i < num2; i++) { + int bits = (200 * (i + 1)) / num2; - for (int i = 0; i < num2; i++) { - int bits = (200 * (i + 1)) / num2; - - if (bits == 0) { - continue; - } - if (!BN_rand(n.get(), bits, 0, 1) || - !BN_MONT_CTX_set(mont.get(), n.get(), ctx) || - !BN_nnmod(a.get(), a.get(), n.get(), ctx) || - !BN_nnmod(b.get(), b.get(), n.get(), ctx) || - !BN_to_montgomery(A.get(), a.get(), mont.get(), ctx) || - !BN_to_montgomery(B.get(), b.get(), mont.get(), ctx) || - !BN_mod_mul_montgomery(c.get(), A.get(), B.get(), mont.get(), ctx) || - !BN_from_montgomery(A.get(), c.get(), mont.get(), ctx)) { - return false; - } - if (fp != NULL) { - BN_print_fp(fp, a.get()); - puts_fp(fp, " * "); - BN_print_fp(fp, b.get()); - puts_fp(fp, " % "); - BN_print_fp(fp, &mont->N); - puts_fp(fp, " - "); - BN_print_fp(fp, A.get()); - puts_fp(fp, "\n"); - } - if (!BN_mod_mul(d.get(), a.get(), b.get(), n.get(), ctx) || - !BN_sub(d.get(), d.get(), A.get())) { - return false; - } - if (!BN_is_zero(d.get())) { - fprintf(stderr, "Montgomery multiplication test failed!\n"); - return false; + if (bits == 0) { + continue; + } + if (!BN_rand(a.get(), 100, 0, 0) || + !BN_rand(b.get(), 100, 0, 0) || + !BN_rand(n.get(), bits, 0, 1) || + !BN_MONT_CTX_set(mont.get(), n.get(), ctx) || + !BN_nnmod(a.get(), a.get(), n.get(), ctx) || + !BN_nnmod(b.get(), b.get(), n.get(), ctx) || + !BN_to_montgomery(A.get(), a.get(), mont.get(), ctx) || + !BN_to_montgomery(B.get(), b.get(), mont.get(), ctx) || + !BN_mod_mul_montgomery(c.get(), A.get(), B.get(), mont.get(), ctx) || + !BN_from_montgomery(A.get(), c.get(), mont.get(), ctx)) { + return false; + } + if (fp != NULL) { + BN_print_fp(fp, a.get()); + puts_fp(fp, " * "); + BN_print_fp(fp, b.get()); + puts_fp(fp, " % "); + BN_print_fp(fp, &mont->N); + puts_fp(fp, " - "); + BN_print_fp(fp, A.get()); + puts_fp(fp, "\n"); + } + if (!BN_mod_mul(d.get(), a.get(), b.get(), n.get(), ctx) || + !BN_sub(d.get(), d.get(), A.get())) { + return false; + } + if (!BN_is_zero(d.get())) { + fprintf(stderr, "Montgomery multiplication test failed!\n"); + return false; + } } } From e8317a5530d976ff0043f16cb08fa2bfc3657163 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 20:14:30 -0400 Subject: [PATCH 32/40] Add tests for BN_nnmod. Change-Id: Ic72e00bb01d254408671b3f8d036be3cd4c06086 Reviewed-on: https://boringssl-review.googlesource.com/8606 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 53de2069d..e12146eb1 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -577,6 +577,20 @@ static bool TestQuotient(FileTest *t, BN_CTX *ctx) { } } + // Test BN_nnmod. + if (!BN_is_negative(b.get())) { + ScopedBIGNUM nnmod(BN_new()); + if (!nnmod || + !BN_copy(nnmod.get(), remainder.get()) || + (BN_is_negative(nnmod.get()) && + !BN_add(nnmod.get(), nnmod.get(), b.get())) || + !BN_nnmod(ret.get(), a.get(), b.get(), ctx) || + !ExpectBIGNUMsEqual(t, "A % B (non-negative)", nnmod.get(), + ret.get())) { + return false; + } + } + return true; } From 5a13e40ab6d8e0320f7257374fbefc05488156e4 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 20:33:07 -0400 Subject: [PATCH 33/40] Convert test_mont and test_mod_mul to bn_tests.txt. That one needs reduced inputs and the other ought to be also tested against unreduced ones is a bit annoying. But the previous commit made sure BN_nnmod has tests, and test_mont could stand to inherit test_mod_mul's test data (it only had five tests originally!), so I merged them. BUG=31 Change-Id: I1eb585b14f85f0ea01ee81537a01e07ced9f5d9a Reviewed-on: https://boringssl-review.googlesource.com/8608 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 168 +-- crypto/bn/bn_tests.txt | 2005 +++++++++++++++++++++++++++++++++++ crypto/bn/check_bn_tests.go | 6 + 3 files changed, 2050 insertions(+), 129 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index e12146eb1..196fed6f2 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -96,13 +96,10 @@ // TODO(davidben): Rather than generate random inputs and depend on bc to check // the results, most of these tests should use known answers. -static const int num0 = 100; // number of tests static const int num2 = 5; // number of tests for slow functions static int rand_neg(); -static bool test_mont(FILE *fp, BN_CTX *ctx); -static bool test_mod_mul(FILE *fp, BN_CTX *ctx); static bool test_mod_exp(FILE *fp, BN_CTX *ctx); static bool test_mod_exp_mont_consttime(FILE *fp, BN_CTX *ctx); static bool test_exp(FILE *fp, BN_CTX *ctx); @@ -178,18 +175,6 @@ int main(int argc, char *argv[]) { "| grep -v 0 */\n"); puts_fp(bc_file.get(), "obase=16\nibase=16\n"); - message(bc_file.get(), "BN_mod_mul"); - if (!test_mod_mul(bc_file.get(), ctx.get())) { - return 1; - } - flush_fp(bc_file.get()); - - message(bc_file.get(), "BN_mont"); - if (!test_mont(bc_file.get(), ctx.get())) { - return 1; - } - flush_fp(bc_file.get()); - message(bc_file.get(), "BN_mod_exp"); if (!test_mod_exp(bc_file.get(), ctx.get())) { return 1; @@ -594,6 +579,44 @@ static bool TestQuotient(FileTest *t, BN_CTX *ctx) { return true; } +static bool TestModMul(FileTest *t, BN_CTX *ctx) { + ScopedBIGNUM a = GetBIGNUM(t, "A"); + ScopedBIGNUM b = GetBIGNUM(t, "B"); + ScopedBIGNUM m = GetBIGNUM(t, "M"); + ScopedBIGNUM mod_mul = GetBIGNUM(t, "ModMul"); + if (!a || !b || !m || !mod_mul) { + return false; + } + + ScopedBIGNUM ret(BN_new()); + if (!ret || + !BN_mod_mul(ret.get(), a.get(), b.get(), m.get(), ctx) || + !ExpectBIGNUMsEqual(t, "A * B (mod M)", mod_mul.get(), ret.get())) { + return false; + } + + if (BN_is_odd(m.get())) { + // Reduce |a| and |b| and test the Montgomery version. + ScopedBN_MONT_CTX mont(BN_MONT_CTX_new()); + ScopedBIGNUM a_tmp(BN_new()), b_tmp(BN_new()); + if (!mont || !a_tmp || !b_tmp || + !BN_MONT_CTX_set(mont.get(), m.get(), ctx) || + !BN_nnmod(a_tmp.get(), a.get(), m.get(), ctx) || + !BN_nnmod(b_tmp.get(), b.get(), m.get(), ctx) || + !BN_to_montgomery(a_tmp.get(), a_tmp.get(), mont.get(), ctx) || + !BN_to_montgomery(b_tmp.get(), b_tmp.get(), mont.get(), ctx) || + !BN_mod_mul_montgomery(ret.get(), a_tmp.get(), b_tmp.get(), mont.get(), + ctx) || + !BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx) || + !ExpectBIGNUMsEqual(t, "A * B (mod M) (Montgomery)", + mod_mul.get(), ret.get())) { + return false; + } + } + + return true; +} + struct Test { const char *name; bool (*func)(FileTest *t, BN_CTX *ctx); @@ -607,6 +630,7 @@ static const Test kTests[] = { {"Square", TestSquare}, {"Product", TestProduct}, {"Quotient", TestQuotient}, + {"ModMul", TestModMul}, }; static bool RunTest(FileTest *t, void *arg) { @@ -628,120 +652,6 @@ static int rand_neg() { return sign[(neg++) % 8]; } -static bool test_mont(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM a(BN_new()); - ScopedBIGNUM b(BN_new()); - ScopedBIGNUM c(BN_new()); - ScopedBIGNUM d(BN_new()); - ScopedBIGNUM A(BN_new()); - ScopedBIGNUM B(BN_new()); - ScopedBIGNUM n(BN_new()); - ScopedBN_MONT_CTX mont(BN_MONT_CTX_new()); - if (!a || !b || !c || !d || !A || !B || !n || !mont) { - return false; - } - - for (int j = 0; j < 20; j++) { - for (int i = 0; i < num2; i++) { - int bits = (200 * (i + 1)) / num2; - - if (bits == 0) { - continue; - } - if (!BN_rand(a.get(), 100, 0, 0) || - !BN_rand(b.get(), 100, 0, 0) || - !BN_rand(n.get(), bits, 0, 1) || - !BN_MONT_CTX_set(mont.get(), n.get(), ctx) || - !BN_nnmod(a.get(), a.get(), n.get(), ctx) || - !BN_nnmod(b.get(), b.get(), n.get(), ctx) || - !BN_to_montgomery(A.get(), a.get(), mont.get(), ctx) || - !BN_to_montgomery(B.get(), b.get(), mont.get(), ctx) || - !BN_mod_mul_montgomery(c.get(), A.get(), B.get(), mont.get(), ctx) || - !BN_from_montgomery(A.get(), c.get(), mont.get(), ctx)) { - return false; - } - if (fp != NULL) { - BN_print_fp(fp, a.get()); - puts_fp(fp, " * "); - BN_print_fp(fp, b.get()); - puts_fp(fp, " % "); - BN_print_fp(fp, &mont->N); - puts_fp(fp, " - "); - BN_print_fp(fp, A.get()); - puts_fp(fp, "\n"); - } - if (!BN_mod_mul(d.get(), a.get(), b.get(), n.get(), ctx) || - !BN_sub(d.get(), d.get(), A.get())) { - return false; - } - if (!BN_is_zero(d.get())) { - fprintf(stderr, "Montgomery multiplication test failed!\n"); - return false; - } - } - } - - return true; -} - -static bool test_mod_mul(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM a(BN_new()); - ScopedBIGNUM b(BN_new()); - ScopedBIGNUM c(BN_new()); - ScopedBIGNUM d(BN_new()); - ScopedBIGNUM e(BN_new()); - if (!a || !b || !c || !d || !e) { - return false; - } - - for (int j = 0; j < 3; j++) { - if (!BN_rand(c.get(), 1024, 0, 0)) { - return false; - } - for (int i = 0; i < num0; i++) { - if (!BN_rand(a.get(), 475 + i * 10, 0, 0) || - !BN_rand(b.get(), 425 + i * 11, 0, 0)) { - return false; - } - a->neg = rand_neg(); - b->neg = rand_neg(); - if (!BN_mod_mul(e.get(), a.get(), b.get(), c.get(), ctx)) { - ERR_print_errors_fp(stderr); - return false; - } - if (fp != NULL) { - BN_print_fp(fp, a.get()); - puts_fp(fp, " * "); - BN_print_fp(fp, b.get()); - puts_fp(fp, " % "); - BN_print_fp(fp, c.get()); - if (a->neg != b->neg && !BN_is_zero(e.get())) { - // If (a*b) % c is negative, c must be added - // in order to obtain the normalized remainder - // (new with OpenSSL 0.9.7, previous versions of - // BN_mod_mul could generate negative results) - puts_fp(fp, " + "); - BN_print_fp(fp, c.get()); - } - puts_fp(fp, " - "); - BN_print_fp(fp, e.get()); - puts_fp(fp, "\n"); - } - if (!BN_mul(d.get(), a.get(), b.get(), ctx) || - !BN_sub(d.get(), d.get(), e.get()) || - !BN_div(a.get(), b.get(), d.get(), c.get(), ctx)) { - return false; - } - if (!BN_is_zero(b.get())) { - fprintf(stderr, "Modulo multiply test failed!\n"); - ERR_print_errors_fp(stderr); - return false; - } - } - } - return true; -} - static bool test_mod_exp(FILE *fp, BN_CTX *ctx) { ScopedBIGNUM a(BN_new()); ScopedBIGNUM b(BN_new()); diff --git a/crypto/bn/bn_tests.txt b/crypto/bn/bn_tests.txt index 1be8848ff..0d353d9b8 100644 --- a/crypto/bn/bn_tests.txt +++ b/crypto/bn/bn_tests.txt @@ -7709,3 +7709,2008 @@ Quotient = 0 Remainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b A = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b B = -cf429f101a2e19a65af1e238f6745215cf476ff2609c846f10289f1ef21b89af2aec53def3f4ec07ea42041f8b5862dc37fd03b2df12adaa8c9f1933cc69b526d47797b40f49545fd093b8ceddee3c55721d1fa19b336218de0cac56d410cc6cff4e620578cf820f5cdaadc367dc4d6372aab1e0ae3831a6d153c14920b1dcf09e7629b7442a06385420d79742e409677e3b82ec58bcbfa668ca072e981e20728a983d84a432605389c855a6668e0ee0d2b67449 + + +# ModMul tests. +# +# These test vectors satisfy A * B = ModMul (mod M) and 0 <= ModMul < M. + +ModMul = ae2ca2ce7addaee2e2b7752e286b2bb6a58b51cfbed5c924f00398e59ec36fe6341cd83da43a33a12410f45f6228079c4aeb3912be87e2e81fa1799151bfa0fea29873097475b2c3efa312145d0bf7e51b2a7c9bc961a4f4dcf0c883ff90b919b87c21099fba40257645be31f95a3a277 +A = 6b18497fed9befdf22a01d988d34213f6687d8a96e86c188dea4172e7c6095a0d18d3c86c0f5a1af9c6e3aaeb6baac2a510930b3ed06ec78ec2e12b +B = 1a058d99397db0d209f01212dd4023ae01b15da04fe62d1f76f21622b2695558c67d706c535ca7f19b36f8ef2d508ffd6cf6fcf25e5 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = c462c7cdd79b7604246a0cd97b40ea5a9a77408f13cbb548b56ee713c690dac0507fd988bf28e77462832f4307b08564a51510d4a951c1ad7564316dbead2b53540090827a8ade8092a6133af0e5fac7310f787dc1472836178ed6992b9f71224da3e884bef8e8379a58e6d4be0fbaf59bc520f786631857213305e23fd5ca65 +A = 16c92f77c139706430f396f72ec7adb045745cd9f5899b0074d9955bd32de66f57c05c7929b575312a7f1c04f19e724d64744bff7b31ad0e6171437763 +B = -8734c4a2361fc530f60b28a5f1c7e93136c5ff6bfc7553965eaca54c61e6befb3c0f8cef4280e780cc5940d21a740debba31f863ded75 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = c462c7cdd79b76042469eb41a7a83115eb84103da4ba438c3e33227631dc185054ba4e607141d1e60990d8aad4e0bb0ceb645ce9ccdfe72d4738cbe1f6a73ed3e070194fa4feca6001c4a853940a227d15c1f1cc153d8c96e90e24805929fb11e0665e0c41c77d5a97fc5903a8b215360e26f6a19922d650f460f7056274ee92 +A = -6715098ab2ba3ea1e6341e89936e3ae913cdd450dc831c8534071f3c362841e47d88f2cd29c0d1239aa0949f3685f12f8519625bbf10b2c7a515e6d00942 +B = 536d4b3e4815ae5ed55bae6950f5a8a61d52439d2800ef1b5ba2285b85ed0f6ec4af9fa0e364a6b14f6f6b8bebce9200467804e787f9f3e9 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 309b3e30f74c58beca8b2c23f64fe1203830db8a7e306e1fa2e2022f0d6d422851da509d1b2936f088f0e35effe12a7463f47ca369bee2f2980bc48dd8e696b2d8c6f35cf55fb8baafc2e613b4c684de26129cf196741aab873f81e498b1e03018a539b5eadffeb5953029f31f8579df7ec0ff3f752491910 +A = -11fec955948e007b59fc50e729941ee9d43d552b9411510b73f6b4faafc0465f261f8381d96f647267f72175883172918b5c866cf1f1ffc43c55f3c96a60c01 +B = -2b3792f39499767e0a8b7a6a406e470a78f97ebb36765beab5fe52e95abf7582736db72a2ebfdb2405e3954c968b350a459ff84ef815dbc5910 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9143ec3e9f74a8eec476cab17ad8636eaa7c60e108e89ae0702dbdb2b255a217ba2530c6fd52658cd931b962054a9c20c8713976ef3b7989c40611cd25b0a9ad0635d61f6dc95dba6e0c4a7d53ff539b623b97ba3d66344fa324f905abb861c6b1e830c4b0fd5f6a4b01f09c8e1408941291b2285c4625267a108c +A = 7713413d87f1e50840255927ff27bad79e5de5898725a876e4647913158cda9f5fa031dd7fc11d2e8130a0ba99e8706341c1a98d5fee3218763ceb1d131e9cdcc +B = 1384e60753dd4bc20cdabf398525e7c4aa40065255c5058cae0b2ec90a3821bea8de672a712431aef5864eab719ba621cbbd8b46fe86fb31286091 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = c462b3b4a0432890d141c0f46a28190a2e30ebb2e4ba90ed132169cd72316b290dbf5c261984d98e63eea6525fa890bf52185ad7f164cf49f67ca91c2f35511f3bef6eb7f3da31a602a78e4752e326d79dea729f4ca6438f2aa65eff44bc60979b42e44f6a301cb5de8fb42abb47bce5633c6ae9479d39c9e8b507d96161e0fc +A = 17d806d7c76aa8acb051fd9c0c782443f1b1b6387455f7cfb737c41658d0459bda5d13587055eafb87ad8d209bccac1fdc392aeca0774ea48799511c1fb9141cad2f +B = -d7c9b6574354e131de4b8643d766641e98554a03238ebfce1112c3da5f049d6c410a7f05758571aa2625f7190b936a214797570539317b32fb94cfd8 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 16c84ed15ec6352a8ce6d5c2bdc0d9f13b333072fc7041146e944a29391f83e346b8ac0bee6dde98a420ba4f8852801d7c5bea6f1177a6cbf799edf2146f8297013e0e796917cc967786788ff12d9c1d07d9ce4b897bd22a1b8a391d3b4ecaa5b5c85d0a03aea5145db6350c42a964a41ee5f83e7d35e14cf442e5d99ccd0ac8 +A = -6d84cdf18a2f53fe496248fafef183914d55c42267af3dd42a39515e80cf29211fd58454986f5fb6afb56170dd9865d3158249090270bb9af341c830522a4dcabfd494 +B = 6f6f3f74187b7d74dee92f79be864d0a2c56d4bca3283742e9cdf15112c8f4208e3ac8ecc98b44b4ad74b0671afa4aa9e48dc31d34224a1f66bb2b4658a +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 8fb782e4883ccf3aaa2d3e020b08993d580c69ec8fe66ecac152c5babc8aeffafe406736cea492450fe6adc25dfa2e12723a3f9baeb02fc0f785b3db760ed28048e1710a78a2ae0c96b67c109c5034375a512b6fc7906847253f66316baa0ef90facc9ab992235153684d49d6939ab9e91086529494d7386f604ed69aca2f53 +A = -1f745c8f0c8fe6ce3f893d77fb274c61b72b2d9f9c5a2eb2467bc00d1f496d0ad469d76bce318bd64ff1107ee5fcad4469f84d658586a5789c068b0cb9b866d8fdcbcac5f +B = -3a2347b491813252e8ebef1bd181534b074a368d076b8c80bde2e54ec3b4ec99001f43080c7857427e069d99b1b65cff998a141ca6963aa5fad1ee632986ad +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 7c0c1c05ae1d6420bd93596a01aa0153000ecce660a8a14d6fde7d4740719cc495fe6681a9a08163b2dfd51659b3ae7db0fbe09504370bfc695457d7b32665a4df53e879ac817bf715d5bd6ca0e242b1ebacb1ffd6698ec90c442910a92b35ec103b345f9a9e5c7b005f8028da4dde80f36f6f6e5675040d19e46aef06040eb3 +A = 4c09264420a9452c6f0b55baee42c076aae5a73697cc6bbb88b7c922f236ee4c18e477f88e2c40cee03f0bbe87d3ac8dffd75f635315f856a3881c6373e8b9a286c813325d3 +B = 10474ece7ddae5c53c4df5b594439124370932dd94aa5d5b4ddaa233b1a55634fb7d72e33bf1b02965fa9d1538f97e1cdb5ec0477cec8ebaf202aff8533211169 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 481543f1556df756ae2e422ffe35aae020c9bde9e9b1f760b43043a4654de363dc67f381c0df1c3c1b90edb4343c47ffb8345a1aaf5dae56f446fee08a0b9ee8c42fff57143e10846610a9925be96418c4c957b4e92af734b96fd6f21974877dba52a0db1fec4aa97640e357434f95ba74b6b8323cbe17118dc489552844602c +A = 11bccd165d9fa2d8b01a48c0ec549a6e600396cd2023f0240056193ad27e971c604eda8aaed6ff6be8be1001f3dbdc8655f1ae84eceb963938ae7bf428eb5c968f584798c1bd8b +B = -cfb6629ddfc98a242e3290959f4d0726c0b1770b52393bc7488a471a90f7f0951362c03e67f443c9ecf4987f5303a789bf65e0fd59cc5eeb9f5d4f40d3e4a14080c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 2a770ccfbcb2bad207d0e2dfaeed04b6e7509daef00a1df88e57509451739a8a0f15106ce8b53d280a4b4e09900420714cb6961ebb0e00e88567c5df50d2f2908b4bf8e0a9a5a8b3c6120503c14f16a99297459543c467dcb67915e0a10e19f72ed5b6891a6121b66abaa602818801d3306630bb04ea57e6b31b2c05e368d398 +A = -442c80289bfbf00db06eafbf06109b55f99786a323fc2c6db5686f99094cc24aef50475841243ec3ade2a1e0ff28b4032fd8afb8bb5e28f3b2863bdb9fc8f033adbaeb5f2ab16fe9 +B = 6d43e3c46f4a55d49e78f40d34033a7f5fcbe50873930e7c5452b6b3b176534e6e70033868c85b4d63052964093214dfd0bda6a84e893b1aae3cc72aa83d039e51c014 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = ba0e8c91a86af1001b13deb115c77609a1e7a3736a6b807255aee898e3100f469ef6222be532dedb1b8d3db4b3b55aa4b5da5629c83e9b2bde76bf2f2a4119a5378b5cde000980b3e58595d988ff776f0388fe025625ccf368e20914fa90dc771c826e4a836b2890e82ac2274471d586b4de5dab3278f0e70207562ac6e6493b +A = -14be403d28c8451cac4dc83fbf895a9d2b74f730c39b0fcb33d7258f99211dde31a78f182ad1d27a559031d67d6f2f94a741f141bab80fc692afb452ee2d502099ebd5760ccec7f7ebf +B = -2742dfd02134594edc6d3025aba5ca4a34dfeb43821ad84164510b43be4fb95748f8d0eed7bbcbeca14efe843fb676882784bb36c889be29bdad9270e0956286552119561 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 20c691d6544912fadfd9894cbfd42745991f39a29cbe3a1cdd302bd0487bf70c0179b9579b77f8481bee13ddbe42f32d734b6118af92884c946ea8576f6dec867c1c251c73777cad7c7c76e90da00ae07f96c8d6a751e5b18157dac4468c05d32eb86e74e0e8312bef85905af8193a3f5c799c5875badbc9eb7ead1258e56d7c +A = 7ae9b4d5151b11bb7bd4d1569a6f4804f3b4d77948e0c6300e4f28d51c9a0afed2ae7503e53489edca5359e2b3d0c82a9cef316cd7e1c1275c31fc9c51a8c1e5fdf23935484e467d6460d +B = 1f46f88d39fbedffa8501fa1268bdf3460aa98e12b629da59676e61852a4d3f8c59f72a2fd717fe2faa09639bc651ba516cd39297e0cac67444ec57c0db47c2a4e250033d02c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = bf21b3cd55c0df8d4d568d00f757b10ef3de782ae71b289cb2b59d36df1341382bdc1825ba13199f2cf279a72968b3bbf5f7e3d13ea9adeb96d81132788231fd988eef04828119dcca21ec1fe844998909cc95a8d01720e883df27f07ef4dc3f09081015dbbdf019b96707c18b0b1db6e689e8f86466a2afea4a9cafc576e10c +A = 1243b14aa3d16a55935f6f8ca49295e35e7f75b03de7192e1e8a479abc0a430e0d340acc05eb9a61a5dcbfe3ce3a4c5c940699f5043e924f282bd21e341edf8b7a6741c6ac72d7587a9e7a60 +B = -bcf08b2153e8ca911096189e35dbdb21b77ce89685484f574c89f1747612f39340bf1b204a23530abb36b2c5e195940b86ef1252d6729393c25d4c73dd434b6dbc3057b05d3f15 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 460539d96c07e72acba5b59c88fe904bf7f1e1648612908444b0b08172d05968b31b43456918b4287dbe01afc3cb4860d9c2fe549a580c989b6507094f6c241eadff910d2603f747f8e289e7a8176ca4a978bba89288a4cf875bf3e03939af966c54e77c28119a39d34a2b7055465f58ef2efe7c82ac547fb675653198e4b504 +A = -5a44cb669c055ba7c28d49f84bf8d12179aa30bbb9db2a48d7a6b09e44dc0e0f7471e3629cd2fb51e5a53346ae025fb49f9591ed1d71bc79daeb3f1254342d8a2b091ae07a758c1555efe59e78 +B = 646cc0f766346aaecbc5147a4488ce157a6d844045b80884eaee9d419087285fa71108b5ab4a05689aacc8d2e3dd0e6714c55eb8f77487a3fc5e56c3c2df0c4acf28a457051118560 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 79b536f4f30f9f7483f90e65e6456ef8072d9a7430405cf8c9377ceea2c676afc338837643436d55ac6af2326ebb362684bccc5092367209822581700d641cb8d331432b761e4c6e22639a27335f45a25ec019d180fc53dfb53d69216d7cfaeaa07db8288adc35b7bbccf2829631c1eebb821e4d3299015c3d462dc17aee5024 +A = -167529b1e8668938ec02a68bf4d76c22dd018c41e19be25e2f821f63c2046085d0af30d8b4212ea0f3f9943be1c14fb2d2a944551107cd2bbf8dda5bf258957325f06277036282977db4575b0deaa +B = -378e1be10a57e03b197bc2b1287d643ba6d89da4bf6a6170816691fb6529c602eced237863ee39659be3729825f032a57eb5de0a87b0894d1a1244523e85b6f50a3d9976dbb038490e46 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 658169197ddd0bfae101c10c3e6a2b10dbb456048e81160b47b197fef439b1e0ed710399cfc80ead8e436f1c0399064f92da50afc335847515686e055fc7bcc0ca721184435955b896b0af4f4d96672ebed2f154538d49fa507b945c0a6ae926793751231980274213c80046666c28ada213a2f87509d1466b8d1b2122e93f8 +A = 49136d37ae8f3da71a6114327833e8aaf3dc8b5a9a27e9d04c953988456e525263f86ba94397321c2093803b789f8db3ed7cdba19c4b796500b979e02952e1625246f8e977e01fccc133f94cb22832c +B = 1dca005663385fc00b4fd58c73adc7589d15ddbcb8cb2fba03a737a320c447a2b21e576ceda73811a31d8277883fd31e22f776bff3261a098ecf8f40f2855b0c723d1265eeafb43f85323e3 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = a49fc8084f3e780537b4038bb769b8db3653a3315298a99c2ede6739a1732a636e9787f2e8b09d0b9bea08fac43cccca71a315e6f4a7d6417d171b4693dbdbee8cd9f95be0847ffd40ff027267125d67b89737e1d0365bef6c4429504d13cd8ddc7810f456d6293c0c57c14a307b94010d79d5c13b92a907f923966fd3c5c8ea +A = 1e7d8de2061cca59d1cc19b356a8fcdf2ccf917e0d81598f014167c5a8de027ccfc8f2cb8c37c396ebaac83ba862c146bb2d551d10ce03de9528f97725804e8a6de57b9d9da811200604c2a032462b6ac1 +B = -e38592f3acd75b575f64ced439d5ef2377d21c61bc70625639b01bf755fa2c6de803ce155744993493debcd4de40860bbfcee86d0b117d7f8c3f8ace68b67cb6fe7a81a145535553896424f7a +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5a99c8a6afaa97d8e7d84f4899803c7786b1bfd2ecabdbfbb3bbb92247ff91ac213a72f6d23c24699d60babe91a7d9cea751e686c027fa1c954474fa5680f0059118426c71299462b11de5f2817d190599cc4b352df4d2e80605f9ad1e32eb13712d3027a2b6a19d52151e37e7fa057d8fe59dfc8a943a42a1756a38f103a75c +A = -7df29221e6a102e32757c18f87927cdc90ecb012ab0557e0ab855daba832d76ddf595b9c5a62988ca968b64fd5bba2a147a5991810c17cae7edfde38bdbb7e13a1fe5206724c05a9fc9276c8d4e503a860c7 +B = 5c586d1aff7dafea3b8ee42e0e8854712c95385374b5bd1fc8ec41a72b296e070940c4160509a4a1699a678533ff3d12299338fc441b0f01e29a48677bfc5aebc644555285756e97c74e1af6aaa8 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 21fd2d881b6a52332dceea42664aeae1ca110512c13bb33e25ba4ec0f39f80eb73b1fa0834c998c23a2453dbff971eadb183c51a30ba78d593f23be9cb6b2b33a554ef31e4a36e0314fc2ec889f18debb956b89d1bf8172553271bd56d89ed0b30abb70e68abaa2c76f73cd5a3de93433747d09c845b5f8843f9fdf9f6c975c8 +A = -19fe3bdddcf08190a037768b77666de803ca4f7f0d7dbe6aaaf334a486dd0da7ca024d1b3df11e0406b0326595a171be30b04574c1a7d04f4d2ccd334663690fd20e4fd168386280510a00a70c1a11e99483048 +B = -33b2400173c057980b0e0cfabbda1a5cb5b83b7ae80708c199f28142237f04b071c6eeb63d42e80eec04b76152250c9e4d4c4f19a048cb9815dce6e66710fad1d27494db5c31d9af37d2aa779d12d7f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 1c45cfacf30682a876cfe253f05b393a2cd4dc065ce73126508ce897a99a723cf5145187643ee62d746f6edf70269ddce3c348a1432316286a648ee9ac31ef87feb14f25c42f2dfc2e84bb5bdb4ec0124e249c526c55ff2cd0ae938555c5f86d856eb181572ed01dc045f1ababa52d249e56aba0ecccda905d7d1e64bf89bfe8 +A = 6a40d948eac2fe5bf6db15d7f6b89fdc0712e32d39a881c21859e8f7722391ce05973efc7c40e2c0d7f56c217d8a986bfdb08bf87bc0435873cfe4d01967c46f7d39464bec411d0369f6f5d1d83f42596fa47451d +B = 12529775e8253ba220d890d4912fb95f91e4edb59610e889431208b6bb42b089cf2aaa12ff9ff98c2482e7f4cbf35b22d15fa28aa288217bf766e937a706fe1e600143087b0a67f668cb7b762c9b9f38c0 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 3b3b08e8eda8be3918bf648227eb0d569dd898729d9cd54deb32b1a1dc69cf7b2c4184c8ae9641f0f75950df263a5e236f428ca86244e617b14a04edd0f31c02bd4d84f25bacfcd4a2786825f0361251475eb6c7e99020dfee4298a1f1bc260d4e364a332bc6f651dde7ce5026dbeb0e5aa75ee98874da54c7930108ad28e3a0 +A = 149d36918fffa682cf90c4d3f3d48e6408e7ddcbeb44e78b9cc7fbb08108f65215761a61d79f37ec8f67cc51e0a9b4bcb3834b0ebcf6734985153f29a2778473b80147eddc813b4fbeb98843f5c1ae6cea68f88dbb4c +B = -ca87f66182e271a69c0964eda92a009d438078b584c3eede28ce1a501838c5f497186d305c09922f32ba858fb55f2a0dbfc9cd0f93b789c1f800cf092726d6d33db19e4f26c7dfca69b83925db14544ebfe2 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = b199655160d88b6b4157ada0e5675f82b33b5592408bb57c46e2f7d8791bfccaa51436dc3b772b83e907c20ce7edc2835ce96595b78c0647d244e9bad6f4184e0003eb0899e7a47ba0be888b9bf795eba95e5073a85c4d20416fcd4a8d4e1e16b403deb38845fb8bf9e9264d68807acf02d579e8cd104cf2bd555e6cf73d0450 +A = -70ccbb73e33a7cec30ef2071f3b1f2e008e70fd6d00fe8b7aa4b9146fc6d0549c57d984cd014c7e0a4ed6d33376998b7c2c9778fb9580d8ca4ba795c88612721c153c186740c58df3fa63b6cf7a4de76e049217218c05c +B = 6cf4168d44a8da8e8446b4420466fefbdeeaf9623a40e10b77547687b25f36916f2c18cf6060c03b3b40e0959479f6aad5e44dcff0ba799262ef53e280f4a7f667d262d472b2e573265774deb5ff8f25dc1822b +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 6ff91af444c61d2e2fe8ad73bdc5377d5becd55074eb60f0f98eca3d8f4be8c02f196b3afea12c36f78b78ae6a5ab677ffb7d9c0bd58987cca816affe468c7fb4b56055f5d2326532d6ed1c00ca2d052ecd103994e8929bce04e067082b4ded7e1973566f99c514b4e0d95b9a8a931ef4f6355066940990fead70208a63841f8 +A = -1c924bea12ad6f8b65abd1796e381fee2cfbec15138191bc22d57165928794bb080c83878fa5fd19a5d657b2fa91165459966f50aabf19440f7d75f027b32e999ff4d3f7a7ce878fe0f33a847d644d86ca19713ca9968d97c +B = -3abd4b281b8f25f5957d1f2fde904457d49a3a7eeceada26b454ceb4ae0e879135d376571f08b5038b7b3d73a9a9fecbe265b72375756a715a523ba66737085e5ef7a4ad988155adc93eadd5d95a0faea56914983b +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = b9076229b1a1241e8b4da3fe143ac31d060785be6ac1e841c2fa9683d2bacff2e2b5dbac33f58b0b1718ad2053c37ee55ea54a9d258ddd8930d2784852844d85db24e4721762839a5c73cfe588efedc8932ccfa585e1b5975083919be9e32a86dbdf5cef84d3d4b2ccaf7a006c0cadca1e35fff2da9da7d7e779494d8f85bf4c +A = 75eb0fe6c07559c2b0c7b2acd7d29b5798f6c4cda64a504ebabdf54bdc773ab28b218f0defc040016178958d5561796230b71edf49bbdcbd3f14494859843c8ca7a0f777cb05827f2839f3982832f4f3e3c5e50af17ecebbbc3 +B = 1b8aa718d61447003fdbaa748a9d86befdd2675a677cf34a1be7c81e4577f665d71135a8a243976a4f6ffa1636695567bde522f8fb1948033a7e0941f833d827e957781cb4349a08c6be418befc8959960fd5fc1b288c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9df82b7c34ca97a3a5d4efa28d5ed4f35484914dd73af9090c4bb31ea3496ece8ec650f4e7b07dc779c97e597e76e43cdadbfc6e72b61ea718c073be1cd204f8ad2bad0df1e530e75705f3d3dc285e9d793c8d42f04dc20773d3fcda8ef3ac1cb10d33d20a91add0358ab8658f49d2fe51d0d2d72684e31c0eef85e5695bb4b4 +A = 1fc2a171445ee6add5c2e4d29e50b91d83338f8d63c111e4d3e95f16d2a33be02bef24dcc3d6ce6bb8f1ef980dbf8fed409a0232c0566153014eef840aff58ed8c33e8d463d408f93e2f5381a26fdea63676c4e5397eba1d39f928 +B = -bdac7a177c77451104852bb99004ce8e617036906667258d85adcbe8cda21ab7d03aa7dcf62cb210a9db8fc750c7e1ad290b35473be0fd607fcdc686de0b78fd9f258f5b25e2ed43c2ad1a38859f882b9f6b293dc258659 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = bd9f3d2e8a1086b177698f87a9860e3a5f030e04a0bf4ee9436ac55e005bda01ff4ac662cb85d39e98a41c723ae542a83a936c3bd0280c6801ffda080ec0aa4230b45dcd0bc5eb41cfcf272028bce3572847637a92d1543bb2b8408e880f5b776e1cf14fa28d15cfb584f025596ff10c9f091c837a3aa622d9e5c856db8ac207 +A = -7fd5357cbee7c5e31fb62ad03bd47b705b574d915200fc7f1013d836b9cb683db020b152ae9464de6aeb8baf14999ac7025dde6173fae6ade325c60ec310eff6dc4130a8efffb15ddae90d760cb7f76a27d0368175d4a44a22f7f223 +B = 5894a0223e4aafe4efd4572752fbde4952c8b09cdfc35137e7e6ed650f8fdcfce9de673853dbf73730b159b2656047e69377d7c5025a6b346fb08831e64bc8bc34b75765012460d8135a4f7a0f41d768fb85abf17f5e2f5c3f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 2c61867bca70e8662c7e5435a5aec020faae86fb079b992bf49d8497fc5f96abbd38a6f04f6ca8510e0160e546b3f68b7baef4ef0f404e881771cc12ec5ed3e3787c2d2ad6bb957cc59f8d56f0afb4bea49cb671cb42f4e8a0ee1dfadb6fa14f84a5b3269dd33e20d658ea4cc39499c7a39a4b5650ad7018d32f97954610f676 +A = -1bf5ae15f24c7c14eb59605136a3f679f303cd5b81e4a27465281d17715afdc2c231d7ccbc59f80ad176f4e0326eb757b52e3695e27c6776d7936da47e3a8a904f735b151422029535045ef489e61ec93f02e6d588491c8dad1cc311f52 +B = -3238dcafb85ce557036d19e42e7e7e473de9f9da6f920e18845dd010546868d2652decc94596cd2c36bd16b02c02559892b9f573bf21ab18c3c75591413d046b385d08aa66d849ab8adc9fbf788e837b047a7ce2b9c63f7fbd263 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = c1d04b831b712d0619db462c3f3fb5973f5984e9a48493ff273a5abe17a548e185d751628899e2851e425a7d4b2c72d4d908dc813cd122b8f497e08e299dca9166f19752ff8cd9840a70155ed9e8c063a3840838b3679f96f1cd5f1cbf0e037d222029e02769dce7fdaea0bbb5417f85497d77c76a387c6b970eac15dcd128ba +A = 7aeb60c134e84f289e419b74f99a5ce5b4aed5fc630d5d591ac7643251ad32d6ca7f052fdf8857f67138262d221de644140e9018f7b84879d74883f8f251303f65e06bb52246ec6a912772cb698b47de41c1826ddd065359f6b9f1ccb0cdf +B = 17f81e53d9fa6201e4d3eeebb32267929cd5258d10f053e7c021c4afd17094f8ecf433b1ca752f8740f6d6bd84f801b1b9fd64bc4787b9ae5e5aba0b4318a63dfe27e92d5a3ade192af7563c74c9d6006ae7701240efdd6021a83cf6 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = aef89874854ed34deae1b77286f9cb0e3017e3ae77fe050bb244acf4f30dc03504c73c1a4d44b769709bdb53811a5d0f8a76a08e6a66fc2cc4e98537ad6a8049f02494305b89a49a55e71fcc3f5fc42d6b478456ada9b19ec0a03f5ccfac5538c0040092771660312be5e51996073ff1a506d7460c57d54e10dc2991c028606a +A = 18d3af14bbffbfcabdaabe44074b407d69abdd80a6eaa5954f0e45fac85af7ced1715c78da872f7a8fabaad3207e31f12b7195cdb25abef0a1e54d3b13349d997f207fe130d7985e2033cfec899a0af310c9827749cd22bd062eb0b1faa254de +B = -85a7d9f08a60031e689b0e611d7f7f46e1178eaa2e6459602e738990c77f4d3783ac43fc04d53504cf67fccbeb02f9846756f8e32fa4a9316b6d3b45f644254077bef096a72bcff17ffa17070a4355121cc5daa2f782fc0d0bb48101db +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 14a85edc6297763547702c212b1a8274b8f85d53ef35cd1b01ed51039bbe030d0a1b9626ae2f571a43f1224d723847a1c6708f2238f6f6fd75db6656e6c703a5acb57f69717efe8ed58a3713ba2720d8c001d026d83de0ce5e24b67c41daacedaadfe404aaa9b672f00562e6901fbd0710c4303fec41ee3338100beb36c9b1ed +A = -44414ec207060d105f599b9a66aafecc5b232b55214c1a5e1922f6b59439b3ff77cd3a327bce4f7406871196b90350e6dca9aae147ce03027dc4de7563c734f111d95171f489105de5ca80047cfa43f7e932917b816ba7d41fb95b4106745d700f +B = 45f2cea1b9b75880ac3ec206740cfe0ecceb488c9155cfacf5885a8cb49be78af8cf221ff8de2328f4880479c031f830a3c9eaebfd83f7de501b7c5cde03c4720c56a676d331b2a13c4689a2e34a43fc11f62825b8776e75d31225ca7ff65 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 7670c1e2e141d8f8f5466de8ae2e0ba2eb3eb7634699eab8415d3a37f8df291d00def88361e9fb64a2f116433dac3ac2764fd62f3201dce4e48a3b7019e5465f82241ffda29d5eb0462fde74dea3168f8993ccd4d090b9c31a5a6cd7e05f725bbc89479836b89379b422250ab049f31c860110df5ed69089716877fb0ad7b0dc +A = -15b4a2f808a85a5bd466a342c4853c04ac0ab73f8e53a4a0477f73dfeb8d7a911ab2eb5d3d192b9b084d0e38db491148947c66f838aa5f460c37341b129137614259efa531c0e6ffdf163ec6851737037a5299060418d96da035e6f583e6ba79d0414 +B = -3e94fdf22004384f7881875b1d8f58019ed8afb1b6a31f5d591e77b0998f3100b34174d6f3466da44b4c7fc8b92ccc5679c26c146b704198a65a88554d24291adcf897bd758a035361f671a82972b5962002c6a828792980f86a64547165327f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 35b49beccd8d2010a8d777c1ff69e28e01a1bb78c6466e717f0a934bb62f9bbcec5ed29f9cd2c14d240a6c33b28c986eb9c8912a4927605532483dcfd31a50876e1819f3d7a0f49bd276ced5c4110470244fca52d2611ed7e31cd8b73e749aa70743b39e92810b3b52320342a65cad3180f6e2966059d15f79e5574348f5f66c +A = 6fd078e3cbcda6a71a710e99204da640edc71a65974fc765999a74ab50a0e4b090d57ed0ee869c8da2cf694b6fab56e87c4af62fbe73eb8890bc066ec3460beba04dac3b8fae7e4f316e8f954c6e8d934e946dfdc9f4cde0f26bb3d40d5c444b03bfc65 +B = 14d8041a3b83468d2f44f150ad8d8d0a1a22035d630f2a17b70d5c3d557d3abc7e4d753e1ebfb3a3ba465520b84746073d211a67e079ec7f47c2cff9c06da69bb5cbafcb6cabe7e0018867c42e07931d6797d4499463e3cf786c6d5d6c8cbd600d8 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 2f6e0fed8a9720fbd83ce950d7545d2c6d5b271582194570424f90309227a51777cac974bca0ad3c1289ceb91cf75af73b0645cc20d71e7789144876b8c1bdd550328d9907accc316189e8ad81310848cddd2dbe362c9398d814a048f93f9368fdbec0f19ab87ad2a59d4066d738c3da3cb71d4716f2cd2336ad35ea1438276c +A = 14bda9e4aac85b0ab7abece728f61450b7779d3b5fb83be813758e742d2ad76597f132aed91e20a75c554f0d61ec4dd118eb733d04942b2548b1efdb4dd22fdb543d9bc1e4bf0574ae2cb2c46fb98cc4835b6a074d6df1a3bc5443beabdc784d542e3349ad +B = -efd765f8ffd72d041ac3244078b8dc4482233e9411b289cbc2cfc26fed2cf28e286835010438ddc9e7021ceb098b10c68bcc4732608ec1f4052df9362176ee14812bbf09ccf7c2882714ecbbf92bbff61c06e9dc35a368208a05dde949fa2cd091ce0 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 1f0c436379f6dff55a59093ff2a0626a9b959e3e3e59365afc33c7a7893f04bca863ec910c446957baa8de4e35a1f4e9c4a776ef41b053f03b775f327eb7e5fbe68bbb478aa4339ae703ee4b573d6931e47e09271d40239d527fe77098a7fbe519f5eda1f26dd6a7d0ee6833efe37187d8a85844690fecf9fdc3a4d80b921130 +A = -51eb34de29ba24d2b1fbeb0a1c324f4ebc69cda2dff971a315c0c2775d988b03ca29891ed0790f3dd507a1d26ead461dade9284613e45df338dd83aebfb66050465d8aee554970b43f7d4e0428e1512289fa1f9b23867b67095c455b66d536b91207b749189c +B = 55259a1122eb7eb611a69118d3d42c2f05dd228d71c0e1e42ae3a8d3d180a95b74150d844e916ac85105805126e4b995f2ed1cd3fcdf28e1fd241dbe3125dfb3e4d90556256eb513a2f7c9b596719c83b26931d92bfd3573560e8bf054138f5d6b9cde72 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = ac321a272d2206df4dcd6ed8ca194a1049c1e3a20bf325fa44809d302170f850721c077bb5d792f86f7ab03ca259567397cc2fa1429771190bb632ac2c92d3fccf6e05e13cd33149994cda5f9c57da155439663f6a13c66f9da553f5038fb92fdba186ed9ca04b8ec87cba4c5a68c8edeedb94e38a6dbe293340dee1a4ecc768 +A = -19ac99d7d51456b00a193b3b04693c7e5436e05763f0154768db078ea5111cfe9eda3451091af213b9c8cc649d341de66c12ab2803ea39655d3d7de182a77355ca444c5d2778f791d39952a7a11839e497f5dfd8a703df49ec4d7628bfc25a992e94a6477e6be39 +B = -286d1d436f113308be594f0f43d7a05120639152b7e2f93058cf602cbdbc016512bfd23f7aa937fb358b7b602d15998ecc150f2b9224c58527c0c1267739e065e24236771e2c683957871637468181e6e896b513569bd004b9845f0f0e4c26a5ca123365e1c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 3466804a1b7d1af8b6060aa93a4c325d9cadb33ebcc8bd991f9e44cc2cca8918411efeed0f005790d649382ec40278c8cff903cf3db177d24466c58cf6a56ffc14e595c36bfefaa2327d37f616b1466eb702f5c49170598bc361d892e18051b8233dbc5b3fd6832befd9a995bcef3b0f3beda6efaf09f7306ec203172e78264f +A = 6710c19330d3f974fc377e28039e0c0ee0a558621fd67fe724c326537c18c66dc5eec60980e07d401ad5556a05688d2dbe7b271f9d5eda3032bf7cb7c420e7b5d65a195bc037090b6fe83064ac3731624ce2baaaa62a6eb07156ca12ee51d4321988026cff573ede9 +B = 137ca18f47a151363a3e8c52dcf024262ba525ec8852e8e406f460fffc2cf88f1999b17a5821849317fcd84d09c88ebb6eb0340120f113d7ca5fbd91c6a40cd790bce7b422552cc0cfd2a6417add2501db1667f2802e5d0f4df824adbd033a90a155cebfbe0b53 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 6f248a70b2cddd9627b32fbd130f05a604866799365f94d97f1eb582b28192959692a870be7c2614536a8de84cd8c1364a75a3927ef9dddbb8c6c87dbf526f2d3a7916384f2daed96002831173fa4a51863c28b4378f99b1b201010581d5eabd66ad1e328cc4e647bf5e0588bb775e130b4a4d029eeeeb5852c5742862ddbc3e +A = 1f014cdd87cb33ffee623cf454edf2c476e91df279b4f0879637eb6e8e5ccab305186de67585595d34ebc195fb150408c4620cf6c7a0b0d9695ba0e0e1d7552ca7d0be3dd678b1cce2beedd11939891a6804770f1c843e16dc2ea6aa8e4043940c37fd3d950caa122845 +B = -8d8d9dedc80994fc5db04d8c935301e47054250fea9020bde8d5fef01f2307cbf458d5afef5210a369c396287c5eb453637a2d721085af3de0d75a5dfb5dfd22fde3b229d438439af7b296b9e68ffc982efc6c825556c52a735f8be12a214a06c4270824d5268fb6 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = a35ff7e232f047e575b200b9fc4c9253de6ac04c612b8a82c275a951075eace5e7d6664fe8f78301d554cebe7b996c1f4ec3ca59d8d12d7196eb3909223de94c220f0445d24233534af1c93433b05c5924799d2c781fdb88c4537bb8d442e6bf76b2d966827bfb4f40378a3f135103513da056bc0d375b1339561700d15a0227 +A = -58346cc8a9a1e5b8babaed8e7f59415388e0db654ea7cd465d96781c57faae7a8af8e7578e46f3a8de7bd1027188e1cc32fd1c0d60be24fa3289a12cd822a6c9a77dcf8799624856c27ba88fbdb047473274e651760581b44457ed048cf76c166d38bb9b2afd3416ac7e45 +B = 61951a16dc6466a9fabae99df29b7229f1ab96b476092dca1e4f8fc8e7404e2fba56ee66486d1f27f89bb3f86f271307228d7d6cbcff943961e177300b6acec1eeb46af1c5725f745a2d2af0fd9642f57a09c9ce6742114be0aa6e939e638bd5c7a92a7c206b2d36e35 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 90b441d8277eb1ed454964acf567067925881b5db0b446a7d554dc61ae87ff979bfb0e58ca1706123453e62ce31284a5a2db1228d259e27abc7fb5cc5848dbeb9a6808fa1b4afa844ab39b652abc41423c2833e1209a1674db518b6df7ebae315dd7f416df54e73088762ef64cc2cd0a08b1cb01c49d9299d149cbe84145a55c +A = -1ebb693ea7d18e0ff4a9a51124ebb78bfa3a4635b75a6387e9fc745a2325409f927324d1289be8a4f5cf2d5c04adc7ead20564f97e453287f03e5ab59a6133584f970446652d05a131d7d382c47b7cb97580ef6710a532dd4f5a0369dd3db500ae5a3c5efb587cf0cd2638382 +B = -3916ebc4653e7d6e0a4f1e234d765d41e9e948b5acd7ebc73cb595559c1b20b037a3c8da0a7aebfa5fd327bdcc922551cdb8db3fb0a581fa0620ca2d2559ccde3ebc44542b4d80926d061e2a35c08c09547e0cd587c396ff2959ee93ea64b1e6b7e2b624cdf445988e1f42 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 3ac61c3a028f4a2df6645acbd36818a2f76a3229d229ce22471760807585a909727411e8b68bfa4e76adc459409a101a1ce83900d46918e8d0903a163de87c07bbafbd60c7f536a62c59370ea53b6cea4384345343146bbf529334b4201ebdc7585b6e5eee42696400c9be9f496406a4eb51d2fd1b40466224f1752b181774ad +A = 5a16d5fb9047949684b80805e5d962bdb939d0d0368b48517a2a826679c37ee0ded4fa83e657192d9ae84294e450f7e2f2773d1f13395169582cbf95860891b9fdf8f3240a16aadd1198e884f22b2718219d478e2410fd4bb98ea534a3626201959af099fa55488f5390791bcc7 +B = 1f67066dd06ed4a49cb556dc2fce22814754885a7cf6c13915d974b46b0e6269c0fafd688f45ed2deeb026a7cbb772c080dfd577d21ed2c81e50e7537a70dd550eb94fcdf626500040da88c43dabce13c82a93769a9e0ef66a471661292dfd3b3af07169e2dc909e43678400b +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 7087dd62eed6ccffc7e1370cca9444dccc4ff160458941aa9f49dec1a2e9ecce4cf50ac2daf06994c5010cf225cc92238cd60e1aed9edb2befb0fb354ffdde94ef5e8ad0415bc95851d59095a5c4850ec52a74c78eab58309f395d3078dc481feb9d30bcd9f113af7a01611b94d085e32193dec738a64c5fe9bdfbf5dbc98cda +A = 13596eeefbf06e9ead8d883113d8ae6cc3da8b6fa13ab66681db5a9c083ef9e49d905ec19c39b149cc09452eea0446b29cc92d4e865e6f681827336945282fa6b276ef552363229a976c503b822e6e4a9862d3fb30dd0c3627ccb97a7046a6a679050a39166388a9daad5ec5555dbf +B = -a4e574363f2e5982cc087b38110d257019962fc166c2d6e6d396220bb308a8a0dc7d90c5cb2ab85faa19b07ed7dc11eae9bf2abde0a5fed279e77a717b43d35e70fec4e18445e37741262d0b0c20dc4375371d87d839d39934f1dc41122e815f3f37352d04d0cf514738b351f02 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 8495eeee238164082240ae1db1e3c1e36fb6621e6b714c9de914f9de8a587d7106b8dc5214f7c60c0ee231d7441e03cc26462e71adf8e29772ac95d0395722d2756f9f64daa8ed41d7ce824a572d7f9fd419112ae823b5b48b8aaae09fe093e9ed05918c4ec88ab159890910837ad0691849b44be95993682b2da2b124de39ec +A = -403f21e1a7911806747bb78a4f20c4e6572d49c6c4ce071db0c8c91ee985e68a16e60093e4628414b2673d25c9f13c4c43600633af95017e3846512197c9515aaf9953570ce5861620716b3d80eae7de0f033772fba82652484cb3ce7cc189d1fafb14e044e07a88da302547f2e623d8 +B = 689d1b4a968b7c00082ae3a29c8571f826c4630c947a7767fe4a71af43a5de84db9b5baec0980eafd0019e09de1b5c56173ede68c9a6acf260bef3d9a03f4c83a33106c94ca7e1a8615b3553088d1d05a62ddab0f1e5a126df5d960f67e3b92981022e1f0358c7970bb2fd5dce7a7c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 397df584bcd3b2e1ec7ed89de624e9d104bd6812901e38c5740755ce91bd54155c0b624c590ded199590be5d98bd1ad4acee56a62d05d6b5fdd1ade12f7db8e3eb08c4a5996450cc1204be7ba61b768af0efd563ea478033324731e24fedada1ad6e564238c891494e85ded4feb2165fda22f75bf120856034a9206511885fd5 +A = -19cc480d1e07523bac502872a971d78bb26955c5453386f5d51767150e229daad3ab2dc85e0fa0cf6e72389391fe627fd2d9f263f105508642eae5a095ec4d88545dc9d0a2c436907460e1ea7db174673000eb2e0b60d57163ced261bd0f6cd8ce54133cfa10591f1fd27996353110060cf +B = -39c45512fc7c9620194fb7ad22abea8f6dbff4a137dc4523115ad7e262934143cf1f320892f8c097a400d4099e787ea7041d0d69b6269d191fcdc8ea28340ecacab71058cb39a9c7362c848826b35ab560c27113fe53c497ca452397891c81365b6e7f07f916d47961e50b8c7c5cab38f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 263ab04c98efac12210beb66b13fec7c260c5b1cbc20cd732a511fb3786b917a617d6622847f4eed70f25982ef5d0b0d13848c62dcf447e3a1d491f4c80e69cec03cd318f6f93134d582210bfa81c1790562053a71091333348c6624d4d793fd6ef971d284a4ebf0be0771efad302015abfaf3edba017907f10ea14a46d9fdc4 +A = 7a354753e39b9ad1c0ad6b65575fc7247487f3ea320fa82d1d333ba8dd5d0ff925331994a6961c9c603be5775ef1842159551f0bfb34920b93d90ca60e6abd514650f77ee8ffff2bac0eecd0fe8ea0fffc6ed0285c9f3c3cfaacf338043975457d62f9c8dda8cce1e99f34529435016fe2ed4 +B = 1a4384f9620567c698ced05870b4dae983d8f0df6aec888353f9dd6ac8ad54340c3ba8346bfa47bac38897f3963fce972f6d55f3407ae03f5c7637be1a34e483e50dcc27148b76ef079f117104162beb191d146ec828ad5c5bde5ee1683a031d554c276d837bf1f2f622cd11baabce10212e +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 91cf4d1899e170bf75dda0d51a6481f79eb94c333b876382c9d04681073e949191223926523f6531f0a45765d7f382221eaa080d7bd05a3c19220ebe18802b15d8009714e8e4e9872223049622ca02040eb041707c7e525f698cc361847c66fe3673a72e4d701466bc374f55fa5437216eb59375c0e2c4f7020149d0118ea72a +A = 12f35c48024e8271e8f9a60a48b5a214bfb6595a837c041b230e6ac87a4c1d4b3f93a2d3a193c750c9857c8627d0f7c454d6c4f224dbf14a865eb83e990b1d9b8bfb729b8d3dedbbe9c95032e4d60676c2baa2aabafa698392590add3b83b521a7a5e7d6f8af207e44ebecd735374acd01ef5822 +B = -8fc18f92c0613d085cf3ee6f586b39b99ecca864bcbe60fffc63c585e5613df68f3534ad46e244916b1f9188507a3692526c9e403b8e93480b0a5a6297f65215f1a5d8e20631a9d559fa1acc15a98c9397761ce18903f393b10444ba51bc92ac44df90d4cf0852da9d75902230c6de6f26dfdb +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9af562a7b61c6c84c91bf979f32ba5d246d2ee2050f07ec2dd5cb3f9496bd37c3922ecb2b5b17085a13e93ab2dac6022077cc18c621cce3a2d2247e5e89de8692a36f596e5dc7a6969a4f3ff0d1580eed380e6550c6218c1938caa2b7ab401ae6f520063c811088504d60a19da3b5018d640ab8d340f35d1337a2ede8bc64bf0 +A = -63bc10b8fbcb391dea305fe61b404d3bebd035514a812d0e1d38daa3d67f9f1bb8f02d2979270cb9147aa51d66ca73d4b5787e472456a13fbe0d568e92b622439d33ad3c357a56dd26806ebda7b3bb592385ca5dba7e5eb5d85eed0a1746441e8d56e22decdbf8f4296e30d222da5af17c427e832b +B = 57a602bbdefcdd00f42ed1e2cbde2ba858d171804da56b0ac87081424ad1569df1308fee7c9ed349eb496d5409c4c46921f09ff0830bc9f57e920e17df16523598fd90314141955ddb84a1522ff3ebfa812cfeb6670525123476a739f64ebe6a5f1fc805a880f8e5a71b908c483a121b38d05cc2c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = b395c9f264172a3653af6637e72c4c8e564d1ce68032a5d761bf546e0c4b51b33cb026bb4256fa639ae98e54e5ff7d8921ae411497272b53d97c2c44b5b9ecc5aba43dde201f64f1d033056f19ceb0cbd04decb486a1d07ab1c64fd213d7eb6db9cd11efd743462e137f368acc4ca0b49a7f85587bbb5ede4be1616889e2699d +A = -1e71df5f04001f6468c3a192086bda948aedd19c5da9a5286856f30524238d95b0ae71940f2af123315ab5d2fc61964d3e970d5858b7c1a78d0f2cfd10cba7ba4830a8c19a09b59794ca5d7da32cd8376b5ab06079b51cd9819c0021ea41a9e43aee147befdbb17a92cac7c7767705fdd908bcd291fbb +B = -394c187308320ba1b14d91d75b8ff993dfd57f9c84e8185f12bf9924e046629ffcd7174879f9925bb643988259cbe9dc9277fa83a25012f91159b012f1964aefddd5a94ac6c2a55a22bbae93085dee079f84cea1d53dc4771901db9a3db5a14eb17c25aaf5377e2beaff6276cbce7cee97a9b8f32737 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 6602ce0fb5002eca37e85b60cc871b7b2eed13d38c20a37a6e0886ee4814f3ce2515f8714c67ad81e8c3abf6a00464e6a51b15e55b6c11296ada43cf459e15915026d3260cce8fb796241fc2b0bdd2b65ec04bee3b7ab6626e10597f3b13b43d16c34afd5b43a219917626c88b24c6f8392bde1b2e65a50b7f1a8dc5eb096702 +A = 4855ce75a3d7dbb72a257f6291e9f6ccc158647aeb2f8beb3e8fb32f6f59af1a46617b77440798562d6f58bfe826d3ea7dd28daee8f5162d7d24ae6c24c2deb2669b15898689ca789e2005903f3a94e991e7d3c8f3ae6181029d959bb15e71d7ba94d2dfd3ddd10f6fc49a65798b5f6ffd64682c78b5d91 +B = 15b3e9992aa3f042fd58ff97a8c04aaebf46b75fdc38caa9224394a1805cc26e4311bfb498d5a04d19396e98d11c8810620979362df82b23a115fc1711b57c7a56b8408e2682a2edca36cf9311addfedd2d0889a78cc1ab170d1379245de6f1f6f4db815fea9130463dfe5283f195e6e81486a1d39634aa +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 6a81ccd82f00d829bac186fb38b85097d52afa3ca83a026856bb83f94d6af6f6c6f3141d433f8fc159d11397df8d2f44c769f255cf8148249d8e9fc4f59ec3bc8e804d7d5189e71e20b8d0e540b59a2854ddd7feeebda5a95f17605e8bd5f311a63cc2e4ce23a51229d0a49ca04982c1bff79c201de6cc6150b690c98106a39c +A = 1f1589c9b5ad9d878631cb03c23ea7e94680220856285668838452a63b726e01709588b38e578da8a4845aa5cc2e4723beafa4f81a1a2e463f67d9a3e432de7064ba8bfcb943cd9efb0e5a136649cdcf5e85a667917075804991b997f318752304f4946d69abf161625ed0c03bf9abeb4ef28034f818e2a643 +B = -909dc7fcbd27d0bf7d6a3d0e2937ce725b5cca0acf78c103d633206cb431e2e2c785aea4bfe2042df32417143de76b71d21587112f36d067f878e556b94ef63d59a07d19647593efdba7f3f5324d64c55f93a283a0dafe080167f6576053f9beb326994f4a1d53e18e3f3e770e69450bb70f276d128e48ecc +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 69139f2e10726f83300505d15dcbad5b5f284d1c06789181683b7b8caf35dff063dfa4968c35facf32a3628dcfc19b3fa4c30ba0e030b06773832a2631529fe0c0c402e05a0c4e9446a8b6c22754c70ef540f90d903d83a2e3592169ce6b5edf939ac5ff25b8bd48aa2425321602a9571661a1109e275a3b3039ff0c2f430b18 +A = -5d02cf3969bff8789850ac898c00fcb3ff1fc49a22cb243ad18703bb8fae25f83502bcdd885417fe46e8237fd0b444712c4fdb8f4972dbf9278a83eb305efc7a8210ce55167c069d1c4136a9b66d0c4dfadbf036c079d12aa082fbb42bfb0098006136a61f3da43aba3d3bcf2f5ac2d7884caddd0cfc28681d33 +B = 50b369234d993721288662d83298d99b9052a0a66336a5a31b76dfb20ec2b5be3aa76f78b2c17c63d78402a15aacb585be5c8d2e7083145e316e71e111fd34f5c79363c4591c247b1a94b20ee042d840c42a3001d6c8dc7cc1e1348e0e3ea8c6551f9d24af2dc2d0c38a54ef065ff048b148ce4f11ed2b549c50 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 87de406a6c957e85c759f8ff684023a0f98e93ad4ffcbc6fb0038c7a7ceed2486f15f36555d286338aab3283aef677118f7cc3f88a7ff0ac9fed31da6786ce895c3c08d3edb652bbc9ac2b44c4cd24ad281ca3a8e8e6e4d730f4f0c25487cfc1b2afe222934eca8b1e1572780dcc149422a88eeb1bf31065c929685a0a97ac3a +A = -1878e0497aa1c2942a2e6956957c876dac73c4bdbf42bc92498f29a006bc92f788c24a4624b87324a7c8aedc6b2c0c8a1a442aa91557aed9bf2c02b6664979e8a9a21330dd839f4ba8f84515fa6f7db9287f7c20f31732b98fc09ee7796dc524870dc35851814bc57e1a8ac49d8935fea04bb08b8760df33a98149b +B = -32f4e94bd073cf3f70810d9af7a873996a0510109bc6fdebb855f27dcd012c59507491152d30849d75f95dd868992c6fbbf29b1d899cfd401e9e7f4e0436732cb4cc9e6a6d6b0cb63fb0bee21e422b7f7b7b14dc5d2b6d10447fc4add390fd3c8e7b06f1d9b181adfa8d04459ed051bbdc9666623b00e3871e597be +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = b456ccf9d066dcf4247a21c7f3820e324ac9cf004cecf8dd1f6c3aa40c2a33e24c423e97190fc71bb9fec21d36c5a687065a7877237a2a05e64cabfb3b20bfff0b1f5ef2e9adb7edcd7140d1047b0919a2c770579ab44a08e5ad9f63a06f90ec7d5885b91de5e524b2e187937609b4b81d40a0b33e31a48d7b9868add75286a6 +A = 6c484e3c6b530dcd3644b19fee66c41c7c2c1dbcde574d87ee13cabef9dccbe5b41e25c32c6a56df23f2e87176afd28249e5fcb918723707fca94d7e2c9623a3493d395db802a1b49d550f52c29666f785652fe81afcab00a60a5b50cbf523cd13dfa06d5a5b0809c68ff7264a2cb35b8d52284172c62ee658e8417e6 +B = 1b4fc753d0530bd07094bae09a02b1ea684fb4e8519086b1e2ed9d59af011f61d1b94ffca6f354a5b428417b328bb1e8af3f6c7ac9121dae58de9f1dcbaa9c73a357f408b870e62b0c7db1a72c4c440f2e6fe90b199b9dab29fc23927190d3f2bf8a7ee926a152e64474283695614ad696c85ea547f5f51d02d1b823e3 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5e7c63276f350f04816a6ed9f98507a78314f1d99081fcd906affa3b8395fb58d029ec657af82e77ef45611bc988095bba9c26f25f8fd404432fecd02398e69635f3315a824d6a98b33eaf6a91f12957a5e80cb48d5b086c795eb3b1e04da5432a7e8be3d683addc586a44b6243ffbb7a979bf9664cc7ec41e75f267d58a7127 +A = 18efe267d4c62576294f4ba44c67a058cdc0bb44c48f4035682b2d6b8a63106081af43d99098ce133f8d7f9cd04d4dd7414f704e32871d43d6e5d73fa9f447873168b43b32d6ad19378d74a967f92ec7629a690d29a62a5a6e734e9ccf5b84857a00d97b9db846b057004b03d88b827dde717fc30e6a5246c752d65dd625 +B = -ebaa580d3eef5361547c692e107439c8391ac0a2d1cec0cd275d0be69133eba8a94bd186ff9a129af3f5a015d5ebd30215643554d7064635dc11ec7a8ed2200fd637b099e534237f0495d2b629abd4c8f84aa1d925d53e98490d02f9fe51bdda08b043f67f0903c0195fcb886c04397d3612e4501ab8c7b7db69f781e169 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 76fcb39f94dd2756e8266c025cebe8e801524a757b976e35ed45e3da3db720061cee9037fdb34776c704ad2059ad8920e400bfbf10eca9bb157eca7750cc31fda06473bd22d4def80189c47ba32e2824c721425f225563df2a2ea1edd090e01c0bf980677db5a5dcad37d21a68e2832d1012586f506480e929b2fd9bb4aaddf0 +A = -75f903ed9bb0b6db8e3be16e797258f6c18f6cb7b16f835f04e3045f7e4974d7a86a63f2ec351c88fadc0635b6dc83a797cdcb5cce1a1674f89e44190991e0930575b19e2aa1512bbbf2ef6f8c3e707b17516756fadb635d8c6bf9caddeba14834b5950a4d1e98bca79a4d15e5fa5fa3c1727d7a49b33d481d32fb14ae4164 +B = 4ccc582c8460f7def2d26167b68788a681c41bdf6dc805dca83127a18bff6f5ebea6db75cd959beb859637b200ccb5c7644d571f436e46a357d027edc9769da226278f7ab947963f7caed1e7e70e572980e960e9764a40c6db67bb526694b084976142471270b2331da563a10427cbbb38e76203d7da5d67487eff701d75188 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5adef30c67aefea4da3884b8a1d0ce6724492bc76b477f1053621e7d19f3cac15448e9401d34e05ac4b508b9d1db9a8d323cf43722e0af6e3c3b6d463c6007449c3bc3236d156cdf988dfc308a1b4911554ecace52938a7b10f463d14f917ec3d9fddcf6d33081745009c59b58aa22bcd7dd8c3bbd489997d4e0bff5473ab9d5 +A = -174e8e057a1d66e22eff88de26f43fde1c8efe5611f6ba4f318f027f5a5818df02ec3f014dfedcdfc8c143c5005c3c5098d409710967c93474f5854c1113fe4030e6682bd56d389ca8b9a4587b8b9262d146bc92fcd81d75c3bfa4281898f394f45d5dd11cd4c7344ee7a933ee346bdaeb6f5188967c388b919a0ce6730c0bbdb +B = -22702bcc4f9d5bc6f803af6af8072780ff7de7a346d6b9293ca751d6ee3a81493fa86738c44cf2b7be4bf14a55a4f8179c35c09dcb1485f4c08ec5e9f9b1efa91f4b5f15a31a46e1ed71cd934ba6bd271bb22bb5703aa468d297f360ecbb48f9fd6c572683e83ebc3d432203347dc62e19fa06f93e087283347950829d4256bf5f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5c2f67b1607776c10fe2c30b112e541c4d8229f5f99f615fa02cf715d3f20556a28eff5c233c58994e9c6c1fcc37b3416b0875b9a62fa5a09a4b8f9e216487203b387ff97fad1f39f674ab19c5e34cb2f162e6b0b0b0084f0618e64928423b73b189c744e3de9fa50d66f45975f68b14866cc16c8c6c722a54420adf027880aa +A = 67056e93b69e8a7b789f1f8b835d9c6ecb7762f844d656b26df9844a60bfbe0d55684f61debeed31a24ef4246485e8a1d43d49eaf97ed9e7b9f2d2916a8d85b8c9e8ad5575cf5a3fea42392e5d1dfb23f7ad41a7b56a4f21e2828aab38a602d560c99783a4f807120292ceae366b1fbfb4be8e5d4561bc8944e7f17ebbcb0fb6296 +B = 1f874f244ed6cff9f910ba9a58db0dc0a7435e8d99ba6412e976b8f64d4106d3c5c57ba079384fced1c261aaa538e131734451fe84fd3cc5cc8b3ab46b2031f888d95084cd3a35a61092672a9118eee4ed1a0df0409e3613b3ef45a8b16b71ec892755dc3f83c5492b67fb9a143ee6102d053078f4875636b20b536d5cf851768cf73 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 7850019c6712f18eab877faa8489daba23cf34b512a3193852508185b13cd5a2e9f503fe8d61b74b5d3930021a5b8c38322aae9b9b1b4814fa4c2c5bc409b58f11fc8fd7854b17baa94a6bff5f234832f9468d90d148fa2bfed774ac03f2dab6a506a70db4ce363f932adcae202f04fdcae968f632dd674416c23d4e21345ef2 +A = 1e378a0f27e6259763890d29e112e3d8d2bdeb9994c49fb67ab680b6e71a52fa0a7db886d3baf52f36d943b5430ae8bcd82e229f4197239c35678eed254c5816722b995e9c311be942f8124e2f80c1e59658433a57f346adfcdb83202e55457308161d2f928b60efc39538a6469f90f1a868cf6077568c8241623896ddc2705cf04e4f +B = -f4ee37e39d4cadb692bab5483ceaf0258b068f2c0354c540438803780c983469ea28324ce7e209c3bf55b91f0a2f4544bf318585e4514333eafb9b8c2f02170c620e9b5280a828ce1d8dfc64ae9c28577e15071825a85a59656c5b47d9a382af6b78a5b3dab1078dd647e0b473174b8415d401543d30a4018cc3eddbfa546d0fad9cbb2 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 4c8f8b671443a3af5ef5749885ce5de8e2afeadef9051bc49c0d7e72922d049b1accdb79d82288e472b07578e8b6d2176d6cbdd7f0caab593dc0fd9224a94920235410501fddd6001b62a7f7d8eceaa7a8e4c0de52029fae68656e8120972b5cc1c2e909c2742e836f2fecfa51e12e4f8a2ec7e69eab061c81785374ac607fbe +A = -5769eae759dd6bf94468eae94189d3396886d4569b0ce264c22d39b623be3abb01bd5008b9fc86701a3373f7764118becadcc69481cbb134c20f669cefeb376dfc489dd4ee91cb333d06afa391dd322abe2b3b715d11ee372666473a473e29dd90fcc97e939049b455be52b3f288db306999019c1177ab5820d94859a9d2f050b7ee1d4a +B = 44adcaf1e2afbfddae19b23cfc0f0ba1f940d32945d0b541db23f3a0a9d06fb1f67ade9a8e620bd96f4005ced99430c7a55eb7e93a701c829fd5b9e55dbb4d3833afbcaa0d9c946916b1a86af4a6393b1155c6439b8b82260e09ccf0ce5d1c4856f4d524983e4b0fa123267694a1c6118beb8be26113a02721a02d7b0ccb01ec6e9c0f9e19 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 51e25767b8d4d7b2b0c2652d9ca6bfdbfea06acba543b1bc8d3d25b2fe5f2998febe1a6e742abc3f482b4267854c2223a5918a9b5c84e0864278283bcb5bace0c046db1d0240443404fb62d70ebff3ccc655e5f5977958df4c878d9859a69731744f3d33978ac31551487270bb4fb56ccbf59402ef9fee42cbc329420180de08 +A = -1966812979042198f70b3f1238c93ac5c6e5749f1108c2bba869b1dac7680f910e56318c9b59be9212e713a348767ba6e75917fb599e929ea2144880d18d4fbda4f4663c7abb49b02245169f385e09098a4e01b56dadfca8c803acb7cc244f3c98bc17440ab2afce318476b80e1d0b4ed9a8d6f2a0be64633f8faad5eb48de2681a38a633ec +B = -2e4f5eb92fc34c753c61dcc826abab6fc4f427c6ac7e73ffdf65b1037464b2a9a0b0290e713d81ab57c0e1dc30e76fdf96046fe10a34cc4511398319ee34bcaf73763a9042fcacf59a100c43d3333ffb3743048e8df0dc61fd0da3f935fadf882ffdfa9f0f42980c1af6edfdf161c4b16087e2b14277f655abe54582de79c51193e13169b55e6 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 33539b5f38a9943b15801d449adabe02da6e21651d96acd9aa40e866bf65015fa40178399254e8af6bb082d021e2a05da0f45b699d193b70112e114f0d25287476dc0c733c5cf9df57667ad0d3ffc4ea2f85b43cd10459cdca9465b0974e578c00a6e275e0b97ef2a4c9886aab7b5947b78a88f84a3f1d8c5f26bd07bcc59886 +A = 531b891fe9e8db322cec59a2115574c7a304c423e6b11516906b840542b2c608785e2c18033262ab9cf68f63edb40ad4f073ce8841db602cf8fae0a6771d741c6392976c9b333ecfcd0c8e9997da40616ae2a9e0c6be93fdc7af0dc0668ded1e42a9f729c70f74500ee76a91d3d993c075c2f645b35792a20edf17c157459e35c0a48da6c4c6f +B = 1a6fdbfed1054a0c5758f92f72db7e5737b0740c4d8c3ae4713366ef6709b21eaecb6b74c92541a9a0c99ae18ac6ef7de79d4c84ce39ad59cea9c203734a99bbb895916275e8778cfcf7fbb7b7d081a677769e4ab96bc7bcf23303100e629fa8e07f5b8fc2e39c7b5724c72907eaad09d3088783b3118e57c9c8ad1799b43a13f73864c5602c478a +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 2eab6018361f557ab06725ad90f6886d4b468ab1a193f8fdcfb4ad15fff781c8681329a27aeb5f03a81d7c404b8017b12fe23165e941ea767c733513a07e921aedf20596763f6f977316e37bed70f6a617e5c2757c229c59b3d7b1fe8755b5f65f7f407f13634aca7c8a267e661ae2f77fc5a95f56cd6c8458119df587478b1b +A = 1cc779145b2b7bf9ef4c9692845e162329940f96eb43e04db8728bfe736698082aae6b6a1b3c32867c293b08547a0941cf4059d2d567840ab6ea526e3724ad59e715a3782ca656cbb739dfdf0c113a18f0dd62423d4edb60057fcaedbb852178d38f1b5a232842b4fc645cbfd97a8cac0b094b870064302dcdf23df2c9e9f736d93409cbb8ce9ab3 +B = -cbba16086b51bd83d3460e51cf193ebc79b826e4f30978274eac3b2dcb04e9d7b56a1449b7cb128bbfeff5c4720bae45271fcc64085d3ee501f0f21fe73cb7db5f275d88be55c339f9180ea21a8cf3755a875331931b75d23f57c2030c89c6f9c1ead431cb4dbd4480564c83f8470610e5673c7eb6c0fe7351ffd7ee460df5db7872c67041aff0227f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 96fd93535728b961b4167be8b304e570cc34e787c12a9a5d76e099b336ed6b837cfc246c5bceb04b0f4744c5da7071fc01d70e342509473e5bd7c60d6046c9b4f21c5ee71c4e678447f837db3a7694fc3936ca733efdb7d387f0f6e263b3ac0b89054a826da9716691c9d580ad38d701d08ca090b6c59be466e1b9833e75d820 +A = -6791fd686f46c3773fc8d7f4753d178a93f6fa4941f4305d9689c2a305bc67840bbef80ff05c7bc6de3a595f73846609327d28540cd705f5aa94a3ae5915ef55304c37c4c43a4b46906889331ee16585629bb303673d439de9c0236f708fd19a977e6e1032e0576a921853f7dd328979ad1f1aa945905dae93a82b3af9451a541f544c18ed2546b66e +B = 6ae062b39c77bebc2fef05743e6d35e14a31c6fe1fdc42d8de2db94ce70a6d60d66263c7414b1081ef2fa6ab511b361b8baa9c71ec628dba5bfd772c440baefc2fbed68d40897878232d9715c4b7e7c9bdd41cfe7b6986d825f68be8cc16d04afb0cf593f3028f3dcd91bc94923f3d7211aa5f0f12d3270e8df8bc191808f0e266c4fce2af97ac7ce06b0 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 949ea5f645ffe5d0d03359d51a663c7dd6e6013812a47be309575e036503126f48677c68c4ef6e7b3f72d76657fa282ad5881263e649b5297da82e24298300d032af3f5e8309ac7eb597b16e257a6f7af3476a264415aa7783433e83be57ffb3fdb404a9ddc3527d6a9c297f8cb7b6674961b3af837ebb65f218147a46c39cba +A = -10f59ba073126d92a201529a5374500612bc59a9e66322c6706b422d35a4f82d97e668b268f5527b4641c6099c80bcea504234f3c1e3fd29eba0f161da97c50aea542becba499f29d4ba5571873d4dd9eb3f48cb26fa6c929a704fe8e49791b2ca3293c2428d9cb453263935c9c90a4a2b39d23a0baa12535845f907d42b729033a0a1e74d18da30a88ed +B = -34fdf9ae6760d4f434d09ce2a7760ca2dda14bc256015809745524dc49d841b07102aefe5a1d0182e3e09d4d45b415e46f653185742b9b8ea6960160752080e5c9577a12182ccf1a293407b534ea8ddd33ad16cd19ba537d8db5b542f86a2a292423d452bf18d82361240a7efa831518184572c5a8b73b108a81d5036b3b530d98bd47c7fb2123418f12e05e +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9ab739ddae55a0d71b39974628d4601122ba6c5035c3ad0439691317f23dc33c0014f3e870a105e4dc1432ec79693bac658433b21cfc218ed411e003990b94ebfa87767f3614ec19f5bc30704adcaf85a9d3d15ea764c8f0bbd52ff388659637746d39859398c79016ace8c6f97d3a5616711a235b85f334fb889b9280ccbea1 +A = 76b15a0aa0f59ec804a5e9a627e1fed524320b29120b6789f8e71b1ac4e00a9a8c826919035b84f87d291e2f35460bee181342136dd9eaeb99ed00c6328b8e44c49ede3921d6275f6e7f03de179fb2374ae2fa6c58852fbb2649e214691daef945ead6c8bd5a53ad2b130e9eab6ad046ddd6b80874ca6515322bc171ee32749333669de0d9c883058423579 +B = 1fe2171056ed4585a143b6b2bb5f44047664f64d710dfc05c18be5840ef9426ef05b6e92e4ecb5544ee4622e9030153dd9827f2f01ef38e62b88ecd6c46b4457d16644ef6d863c226acfd6928a40de614a5853137124fe69127a7f05463eaa49bc742d8f7be300d06b302dfb0ba86801119bcdc01b516afa360aa8b22b7c6c1839cff859ca1bf26e3f7e030512d +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5631048ffdb2767aa04d59d8a5750016b38b983a2d53743ba4de5d93bcfc8ec30183a84bb1e290ef9c72c7ad357728acecfc613a6f9b3d712456d545ed54a337930937f4589fe41e66ee930db3dc10a4fe41481008c69eced65b9d1c46b8574c5ac8f7d94025d8fff00ced17a5e17508527681bf94c2dedd51502a2c4652538c +A = 1aca12b1933f25ea081e12ff4a4f6f9ce379f96d976da2ff7b8eb8ad791fabe31c1148fdec22dfd67828e540c955a1e13f40c5b125e1c7e6bd839bfa84e5bfb58bfed76058c6db77af7a34ffd25fabd60e19f65e1faeeea6371d7785f2e5bddc8650a7492e06691d61f997483661eeff54a30656f1daacf31182486bc40647975151fc05d2f64b50e632f5d5c4 +B = -88ed894287043e7e5cd2eda3c1e5c97f85809f7a246b0c20891fa9a024f3aba4ec1f3d112580fe6ba6b0bdcaa1325ac7ec9508aa88c187af08e4f37631eb6cc97e4481b18f747ce6d35ff355e425a4833834ffb8d34a818bdb015fb818ac9f58feb87020234243aff912da5590ea3f6cba74f1a9fc3ffa2b4aeea25479c55a3b572621e75d86d8c8f6ee4f587e0f5 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 6ce341aa4a571cd5bc110dd436acaa09f409661967de0bd096c77c60db58b2b0ec95cda50acd7fa20ea4266b2c579eeb6ac214a75d40abbb70845db74c4d6c93f8c545add269d45fb15d985e7e630d0425565d06dad4a3ff9835411e51fdd9780c24f466dbf29244cd1b8c3445af181d0928db399bbc8632f7ebcb9d48c0b754 +A = -52c53999b02a92d6254557203cb31a21dcb896495d1f29f3277d19129ee43e521ab9d5a297204a844a9537d63b74686eceba72ea2e7b98ee8895513395cf7c44c99348f5c4eb657874a8115f0027d6a416b8a04a1ec0e6809b7701ee7d41e99996e307bee9c295ab3df1faf674e0067d0ab3bec4da998580203e33760870ae472a3045bbd66e352b8f4d284efc00 +B = 4329d110504caeb71ce0453b0706ff675f646e70a6bd9575791a38f672eff226f4958f8b1fe4123c0001d8f8595d8030d0e9798232942725a9b9d654ecf50546adfba7103fed796b455ffbb4c153e70f941bef7953c8a210d6f2f4ddf5d9a79d9938503ae8f24d69d5d7df1c988630ed960e12dd877bb80a1ab0bcf6db67e0c0578fc0c40408f72b19052534da8d31ed +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 4b9fc1e0eb4be199427c48bbe1b53948d0135bc1965b8aa5421a4ec704b13cf934c650405ba02ad611b0f29d46d82d4a1fc5a84651a29364524e37be2fc7001cbd3c792aa477802999841ff19620cf66dd2453c9b05aac349b9094d43b40e358f32805d87cea3cfa98e05240ff95ec57d88e0a12917628ebd34946eb1ad6799a +A = -15a223b691d8b3696306b0ccdb52c1d62c7c2d1ac71e5f07cd8fba960417b42fb5ebed5eb9469be67f231b5254bb0fcfadf5ac5d2906769e8bf8292f0442986cabd88805a162c0c1f60f9ff0bcc2029ce33452d05f754375c0bd147fba745bf8a0008792d4f90d0e0f2cf391f2d7865705544f4a220ded44732321473c0ae7870394d4e625df11bd0923340cb70b995 +B = -340e5ccd644849d982bdd455ddb3b9a23ca14e168bb87256bcc370ffb6b7fe78fd062b3bcc1ad3c8c3b8cb549f2baaf1b7f0f6522aba02fd35b651f7de52b3aa2e0e40352bfd6ed0f84a2bbc3b3a396dc8512ca1db01cc69611925f1037794c82a418f10e0d994f458d1f19051e8bea32b90ce744d46718f42e711c094ad0a1ee96c88920188078f1b044ccf307e4cad7de +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 31c090e5160faff9a136a7a482b42a43ae3c7d00c215cbdad28804be0e7b12b0b3af820c1350b1622a22c8875f24d48ff16231c826d1a946c66f70aef92d4e6582e3ce9213d907267251ac74fa3cca9f1c8fd53fe9898aec19936a2b797fc345d68f0791cc740199be39c05053d5591d874b415e62653b04a3f41e263d00f230 +A = 5419e87e50b28b6d24927934b541d8de548a8f4ec7e9b00aadb6d23f2d33406177d3fc72d29ad2c2e141ab2916adfd30ec4791c626af61d8d192276d632aaf3b54e2ffe83b44f6f1ac441e6823b6b58cc08fd7a0af945a02eabb5aebb2c7ff0622a17b38077cd0cba906ce23e71ac7f4da40ef6066565b4cb3a62ebda28f3629eaa251dbd9979b123a5447ea20331723e +B = 184782ba4daf429cbd13ac13fe93fe5833f09915cbbc707feca3293e505ce9cf0b4b12ffc8b178e0a4617f809be53d4895a4182e7a8a65043361e654befe8b01429ba4b7420193d1d7d90930ee19cee0316f33a5795335f5fa517e1ffbc99b95101b0f936353afd3bcfec34851ebff1ef02fea991a01b587d28640c935ec91496d1aa3ab8d38a6ac75b3a4198ed27b9019bb3e +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5eb9f3ca660de481968a3c7321281f22fb9273b16fc10d8eff1fe34842364dabcfaee4993c1c8ddb7c8d6e509a8d2afc005075d5fd3c4471f0622753c7797aea900e785ceef905e2606f64f34e47239c40b74f07e2ca70bd5a18cb0a88780489f3e98232221f65ac9c5ce703a256b7b75eb1dd38778d8bc05a37ac9ad8d36b35 +A = 1c73d8e3d5db127a81477a5c4c6d61ac62af446981773ca15a9a01fd5175a2826a8763f91d68df28ee606e8ffc203305875a238d2095345556f12f3b5e10c5bb6ce3f90342ac74b9ac057195c863c4b9d28ca1d958a98649c7f8897bc6abbc39becae963f61b33bab4fd20d9d0e5464f21c2cdf06d00f597dfde45dc5919f5124f26888b12d72cbd2f57de3f2de7c014f891 +B = -e406fb60e35f0abdd313b8431f4cc89fbb034daf71fae0cc727e9a93cdfde53566fc74e48f4cc2111fad158c63293bca0b21b98416381b81d2443d0e91647679481cd6b6869b37112d3b6e575eea7fbb5bdea422558d817b49ac36a829926553202cf9dcef09423c085d26176a89be741ae20a434ea461def090dbffaf2e2ef97bbd4ec779041ed69ec07d125c7b85a2d215bb0f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = acf9d363fc9b76ecf7e61c33270031340e66595e559dd1c9dd4d2243819b660183521a4124558fd4b216dcf5c52c4127fe517c48cef428b9ee0f1bebabab487c968a80b9815e82c12e807c096974ea3893a8d5597f745365c352a6bc6ce92479176092f02907538c5e784bf26dcde7672338f402753b08de8aa21b9480df6955 +A = -7c03ba6e3939ebbeabd35cca277eecaec31f326ab75f1a29e05af50c4e62e0175d4d6a57acab87cf1fa3a51791e9a2b2d4d5db570ec3941263902b0c74544c323c106557cd5139d2a25f3c3ef81ca009d4e3c16f1abf6e2b5196df1b30def46d61eccdcb3741a6dfc8e8c5e6db68ec29c82b0adf6e35ce7aacef8da806b3b58bfa489d319869b20768f8eebb604a9624d048f9 +B = 4e021959da96ebeaad17f9896ed53010d80ed3fd4c3a826a266e82b80ad81b3032303e7c0e58034a652b8aac00c08d42a530039de60d74ad349438f5ecca1256342ded6f30e3bd2aad5bf2b49124cb27f45f697e157550dbbb37f5aef0f04839aaf1ba43bf1e77a1529818d0fa91d940904eda6b748e5c86cd1b37592542c43b7b4afe2b8926fef6dc01784fa431d43900edef27f8b +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 24124c69aaabec7a7b4e7a82245f6cb14b199852a8b314a7b8d9049cb66096d5ac93ac75eb58a2004de8b0fc8375638c0878fb6a45be8bfbcc292e3571df1bb8d6e346d5595fa395fef983a365e4e868154fb3e337d47771419e7f1dd5e4220900c564d7cbe8e7792ab288f99d265aeb296c5ebfdaf08b88d9b30ac660cc3ff8 +A = -167c959417e9566c93e7e05d2a410f4850e3a313e516ec958c3d2fbdecbf58072d05691c68981e176a867d7467091dfeca11f695f750c8c44ebc4d08e39e679d96c4791ceb1ea3b89fa3ce26f7ef214c5368c03ba694f7ae592bcd8ae53a66cb3eb1e0cd3c105faae6eb7e7a8fbc88248be722406f2d35e46c751b5ceabd992091eeba15191ccf6dd61a7ee0c624d43b188c42b6a +B = -343940f3b2a5f73a51d6f609e8af306f44ce7b5c2e79edf6f4dfc07866dc5c4b2e0ba48099b5503af87762a44ae451d166f8914ba25b3cc41a766583bf73d27e40784064582fd9fe952fc00e9aa2d4e4f1ef35818978e725e69c1bcf267fda4d635d1d292d54d3ad10bae9763dc5d7f7226f371184465695f2d384d749fe07967a1bb64df22f294ed88b13600c7068d881f713cb8e3ce6 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 50cac148215963e58cf6d2ebc36fa518c63a0ab8fb136ab84c9657fee459043ee9f42aafec89e8ba5fd1cc5c4495a41e80590ce197e12c087ff7e6ea88ed798735f55a1634562b82f8514488ada526e5dc10700058980885000e266cad55948d1e080f6343f84b12a3698d9ad5427fad4017d931df77ed2e45e2fb8380b7fa39 +A = 6a9833d768a22ea46aab1a1619f30283a1ec254a2de5652981d73146aabe31041ed04d271c6f2e5e2d090cd615518a06563a94ee2b12cf9f142de3f15599998a712974d0ce9b122a2aa65bf8750f54c6324f12e321a888154330f0f9e1e5b7999acd70d4e6da95c2df1da2d19544b7abd2bd3041e3228c7cdba44f7d1cbfbcf968f8fe87fab523eede0485efaf5cc9e56095cec8983 +B = 11e782e2b3f469b1e3d14ccd1b8301ffcde7e371f6e9afc99af5809110c6d70e1cca5c0bbfeb95fc3ef8352581c11ba75c0f8c445ce2aea903769a24289581c95ae5ebd9553fee61a30d155bf6011278807833eb2ce7ee2a98fececa23fabaaa259409e88e3c4f4eb1e04176d44878ad3f6961e0615ade2fe86b6eb02adeaa7c9019d63231a28f84b7dcc8bb0e71e2a717db09301e1dca20f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 7cd49d72bcf5ff4fa2c686f21e1f0146c4f24b9ad2e900dca1c0a5d2fac5047509064e65ac582946b251a3f04850c9abd8b80c92af0fb11ac13debdae8b94927f1de0e4bb217e78f5d04897c6a0762667d3d883cb754dc610442c9dbd44228a7ae4f14fca145550d813655befe3bfeb52f1c76f989ea8a1dd9c10fbc7e9d6574 +A = 109fe33568598972063279b71ba0efdc2e03f770cdec331428fb8ca084c9b20d0fdb5cf9ad7ce90c8cb8f0fef10d219d7dfcc6b4599440db8cff9971da7852880bf004266886eced8763b3569720df3a1fb0dde2717ce0183f2250034871146628430f206c12f5fd87574c206b203d90c0f2c705cad3484c73da8bf4e9f7e1bd433a6f7fd27df63079d30c490aed7161bc594eefad4bc0 +B = -b95da952cabdebe0194b7fba519768e1b56149353cd12023b97397b59e0d7f4dd1d27b65b833948f58e66d3f6928cc3140cced835dbd612cc82a7e9fae1621986f71ddb6707ad57926b03e87e165d30fb145795a70627975bbf9d9ac9bce07492de5227c666663cc28b3e70b19dbaba7f16849535ce5fd61e91cd2875e0a534a10c60d21f919d566a3469d108a35ec3f023210efd5d318c7210 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 98a89cb3c9602fe503c32c44609bd4487b6c8323737b3376dafacc3eff96efcce7a31f1b61ee6799dc9561e77ac058fe5195cc013e72a2864f7e492d9f35244b321d46270a582f6f14f15fa8203d392e81b183a1d64d48b51d70e38d49c93869ffb9d7509f15ccde547d2d9c4dccd50eba49190b6e831a9f4f9000a95dc83f3c +A = -67d7fc8f1766c40bd476cdb65d4dd161c3d4c2c5860a0c559f0e87ada213c9ed33308c36bb1c7d615fa69ec53656bbae6b57181a0134af23ea2a75f8fed3290a2f483392a3745fb57adf2121738c84f6d34325121a702c8ccac0090ea27fe9a5ebb6ba9d4f397e4a7e3151850b3d7d25643398bd3e4c1da081471389799245d986cab825a2e6ca72b38ff978a2753c835299ab4597bc65fc +B = 676ddc4d18960817ff8fd2adffaa68c87d234d62d445d6ba3847ded849356d929d9e4ff01f517d7b1c0778bf90f475923517d855956f17ece1e032e2fd474d2133d6b8a591995454d8b587cb4f6fdd0fa29305f146d340cbe6b6efd28a926c73735621be0c5decb792083b3f063a43dd9f635e03f78c1bb56389a5cc993c8f36134d755a324d4fccc2ac3bafa270df67db0a4ee6ea4497aa33b5a8 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 76c31404854006a7d55554762094df6e11e0393f5b0451d85de2e5b104432df72023a35f44da10dbde01cebf77b8f9d3ad582373c5d32232564729af0d03c5450e439045d96a2f0a38871c922af2bd38c545d219adce0ec80fccd121d6a733bac09253604a8a0b1ecf0f24e44b818ab9e9974181cef10e9eb17684c57d72257c +A = -134e8784878a8f3cf49ccb952075f9f9bcd24a20f8883955f262867045c11a9c566abee00638927e5de924872fb98f6376e321ebf3f567db6cfeede62e04f839617d78b7c9d3487b60a0d3897b3fa49b14c12511d04854bde4a9dbe5f31424a3d05cb75d23b46f6c0819536020880afa5a2c173f6881754b56f82a2864c99c820156f96b5cc4665d603597331d98d90a52f4a30c6215ee5eaa2 +B = -3c5c0d35de5fb21c84d2db228829f43b31132b582556b92b495f59df502a6d00584bb5bacd9b8c1a8c7eab91db0ea24b40f07e62a712842d5c2e1d208a6412a068cd5c6394d715260b67fbc03e3ae7eb4862f74f4d7484f747774fff03830c65fe022d579adb6737f6dfe297db750e6a58d1004e7e2716838befc2ea97179ecd53b7f36e3540e1c3a0f3e044bfe2d0efa9b89d2d308cbd0bd88ab3706 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 5b704b3181e5d0494937b4d6aa8172eea82919fd1d884493197a6a85ff047a7bcd5dcf072bdcef0287be20d4ac49918d1df550d184f86d7220f0a84fc4da3ad05e131c443fb529df01fec9fe4fa6fa2f36e791f9e16b4092759016d2f9b1ae7c3d071c57edf26386aaead767a3109c12a5004c7b9fa595e6d592daaa2dd1df04 +A = 48a0ccd2d14e14e2aa862d306501efe5de239e8ef36ff6251c861a0aee9f739411f402491bd99aebacdc26c4f30306f9137ffe4579c2f13efa81b979ddfffcd23675ac6307c0aa3ba8ee77a2e3a3c8e241bd2ade6484e6ead32ce8d752fb3584d14688f223758c5cb8705cea9c56136b219d87f9904bb56be2ea1c9a035df33455206e6b7972cba32ca4c3db41991117d88da3521780fe65c4023 +B = 160120a35ae3edac3edbede9ff1c6f317d95481227d87785b7ee46cfb80fac9973e418244884caca3211a3f6cd3bb419cf70fbc22d82ba5ab98ad80e1f6c2cda753aaf7be78613ef25577107a47ad1ee3c3645db85c4d29bd77900e99e1f439cb23c6c68662c05322f94feffcd9e37d8665cde984387093a043447de590e7874e6acfa37ed302040df4d5c3dcdf9fed91b3d17ab5c141d4494d0f301b508 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 448c3a64958b82ccaaed3c74706ce0a48c5e059c3610cc03a6b5a03a7de5d4f1d1e4b08a31478fa8edd58401f0171697f0662146ce2b371e335d695f9e4a671255f29fc0b9b7d1b2eca4cc7f8357aa0920b5942e31bcfae84e909828fbe5d02251ddf10dbe4c15351f675e96e2eae6d044da1f0858ce8ba9b7aa146850b85d93 +A = 1b2a52aefe44170376df29d17ae2dc1501c9c296f72f271c21f53db71247e72c3eb2b780190c45343bcc8f548507559ced3bd4a6fb13f9174dbddf965b9c4a56c3d88727736d78be9db2268cd02382e50c6fa28ddaf8eab9f44ad45d5882a5100b3027c150a7f3bb36f29d24a76e40f3820ba116d645800459f06c20679321cf5be72450879462f0eac99ab6ff8d26b464cd0e6d78621c9263394c15 +B = -b7d9bd08d7d8e0e9596851b7e03c78973a502afcc7b5fe5b0db6034ebb8a11df1ef7ed0ae1371eb4111cefd61c61935d768be3e3755e481daced219874cdf0d07a76e7144be626cf1fc21c8a0e9db4389ee213193775e95d4d86741d8d8fc820c239b7a90937000dc3e89b2fcd61b44e1c38c655bb3d31aa7e422b4406c9e4a88e6a2c18ec7c048f4a6b5b270c90d9fb378f64be3b5b351621db48a6c18625 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 2192157490ae044a26c23eea6da51d3a3dd08c7fb67a9beb76d37ee24ac0089863aa7f00849b81bab8259f3a0e1bc744d841e07aa413c286e4bef2ff3356bdbecee756026915894584b4fcef7e49da4012cd9fcb5dbe3f3b867cb6a7ee959a328b0fd56a9eac1f4e40a22bf0a30073cd2d48f99245ac03c373810c54eaf3306c +A = -598eef47b40d1fa1ce260edc561bd1c1ab286a7e068af412ec2baaecd07c5b9cd596505ea1bf0370ea961c4ceeb9be76baec74e6952cb846f20e5da406bd01368b85d59569b403b7a305cd7448f331f10a34def43c738fd633df9a3eb194c32d53aeb567889927271d71d3929d43fb9338248b64f7d23cd1b053239e09cc2ccf5fe9c9ce240f1a10fb151a8583e4b4cbc70ec3082dd20a9962d564544e +B = 559fc917de34bd7dd7a23a432142ed79e3ac4a6caa357eea21e423eb9af7fd94f1eca735d2588ec4c2ff013520c3a0e209627217cc69bd5a07ca46a43ec1f1bdbee5f09ceb1b2c18bd388d3852e51070943f16152a73da624be680c671057677356c6f281a4ba1f7c60609125d7fd9086c907ca5c191820d80e483886b70c1074e2963c49996ee92577334881edafd88270bb967da795aa4fefb739e4367390ae +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 3488bf00f67b852592922fbae64fa56d2e4e7081678e789bbb3b4f48df62576d537da2e99c9bdd721c725b9a828194662bbd51ee20ba73d4ed5562482540880686d9fb1e8ae62d08e39fdbbab1d18e399ebf07b3a6559dda8b043fc25a8152858d39b10ff64776e00a839950e7a9ed5ea95b594b6e9e9d4348ceae08071ec5d9 +A = -1b135d8cec9969561be396323e2f8be0c60903ca59b6c418cb19876e9e3cdcb9ce4f5251eadea11fd6e785476c70822aebdc94617063d161ebe55584a8a774ab230b8228a2b65bd5a6c873bb6b261429eefdc7d0c64c7e78133e739efe57f835ad03ef8f84601e1a2310659db5e0ee706f23e3c5c38c9f8c36e5b15b654d1cc528f1dd392f1b08921af8be6fe4e4e6db774392441883ef867bc729338943b +B = -34fb63435c90018e5843098e379c76ef3ba0615b6b500854b3dda3e77fc5646228fcf3a6e1cd87a506e4959ab05e24474990ad98ad0865942737734c03dc289307f1b1f424b9a8c2264350943449b3d2b0f71f989039131e23095d122ae98c0089a184dc530669e804140134e5b602861a5e61c030fc3d3b3eef0a59f8c0579fc9b0afceaf16698de3fa07c43231312254c04ab11ad7a29efc4597780c2cd1b64b43 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 8ea5fcf7fd41803606c95729d2d910941e43b222f9b0c93a1a803b197fababbd653a92ee34e805906fde29b307a962a294aa4dabebf0d181c046653ad0fe6da1295eef817f3289dcc6579cee8869198c39a9f79992cf6894162d35d812df327a64470c935994aca4985d0e6a783b853ad762338dabd575ca71034e29d768d014 +A = 6858d029a62b0f75e4c59f3ec067e3990b2304c90a097daccaf554abec49a9d297ca14648471dba08f22ebbf8e238c89ea06f188203599aba56611eb3d4df09ea795a7e28f91f4a9a582c6b949c6ffc584a076de653446aff9b24e87202037974aede37aa9a121b5b70a3e9b5ca376c9056c2c91f5d5484baebb64cccb6a09b4f40529afad1ed64b4cc4aca586892693fb5f92edb6b4d5f678f7a2441e51410 +B = 197d6deff7adc30b025e7e418cca0a641e1a1b35f78fb56b9d8847f0690313475e6fbc6f73c3a718b10bf37434dd9fb1eca33a99bbba674195b20d35e3b34ba9d7c8438eede24ebb48e6d39eecd93fcd7dac44235ad32f208919f57b261da70ca378f9b03ae5e5a733f97f0b3f4102d971272015bf50b6f3e50c7b36cdaa14a8a580366c9cb0118ceec6e627827b0b8f614656292675ddb66e1c55355d5a1d78e69ed31 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = a25db977e7a8fa4578fc530995335411432ced67e131fee2cd7ff56970df64a6f0f4a7d225d2f4ccec8e98273ec9a0f1aef01dc0b866e425d64e09cafb9ebe3f80bc0ad71c769f1ecd5efdb4a990ebd3a94303f52f4a97e3a1d615918f8b2df5321c4aa9339b4453d7a710a803106dd0ab49c6cd9aea431f97fea9fcae0bbd90 +A = 13f97ba15ce46ae32147a0aa4c1639b6b555f4d8a1af15ede4f1103f7a0b06b4625bf456d667720adca0c4e26e858f008b012fae63cd89322b33fe51e87714519e7dc3cceea27d968b46ebc04024d063b17901a7ae978591ca6ca41afffd81769f04b714134cfaa6700cf23bfda6ce67313988bba5fd3782bc62f76cf551d140c978dc002a779ae37400d34cbea013a5d1338b203ff267861edd88ab8ee1e4c4d8 +B = -88d8a4c8c680fb01f493f73753c70ee753951d4734627da14962e36449db5490b8c575729fafbd203a125b500b96364e6799d9cfcf0efb4ec877e86865eea5e99e2fe5e7655c1ee0eac641e73b71c66d7a72c2934d1ccfefcf59781035b2c7b89e5de3f7d1e9128cac57947d22e7577832ba374492a2f53be37e17733d8bc625fa77fa5cf093975049a5c477f792fe75e85da26cceec820c8b255df0292824b4c3a8ed455 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = c1f2165a402fe9becea284dae60453965ce327f540bb8969562485fd1bb60372b8689d9c9c97c91bcfd699dc370117ea8b704f06cae3d972dc6e5eaac971597c69d4dc24a68b256f97229e643706aa6d2d844078a5fee2d08270820055ea58155d7bc754f09d0c6f804e55ebe53e3ec418747d4130cec68533f6f0c2f8fd2409 +A = -626a1580e52ba52a877cdcd62b34cbc7f949148671d4a61201e03e98985d704b2975b9a2d9c4557deae065becd662ce8448171ac582894bfa2c59d4ed20c6d0471fcad1d0fed1291df5e4556aba72f3645486580c8bfd0e3c8f6cb34fe17ccdd75fad4d4a2db4e00bb8c2a23ed17a31e95631320590f40416c153efdaf897e3b278a1faf1917554d9292f90c4edd5992748b58492289eecde1af34976ea8ff507fb9 +B = 44c336d7739118340048939d6c198f73f90e13030b69be286ef920902391d87a58df3632091d0ef25340eab395203e8dcf3389e95debb7432165147e145735d2e3226637b4b8cb7d85d68308be07f217f57fe439b31fddf3fd469869a20f1f852e1645b0d4903432ecd1fb6397db4c11f6b6b9c0fd25778b0ff00bab9ff576b16538a6b7da40f01fa7b987af8ead41ecb66b8940c0e8a1208d0026773e711153d99348e92303 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 98eaf476f11168bb63fddf7dbf3347e619f9b580ea6804ab893214e94ebc089cb652e307f1f37ea7ab9052a352e260ff7d1e8c17461bae68c52a8a8f1a57a84c79b2c8fcc2d504ac4f553d2534f2a776ca129ec1942d83c8ae24c772f6a8429bd61949ca1aa714cc3881ed731497b84415c88ad4b9be34197a549737edcfeac8 +A = -15897a5a986641fc2cda42d185d72aa1552eb92f788bb71cc74c0e424bd038e02c620d0686ff88ebdf0bc1632093c0d89e724e7d5b526b0ddc4c7e145aa90b36be0d8574901fdf286df84a6b52674a78cf21ae4865618b4347bd905461d878537b33cc41710ddb290964c48e44d4d2ce2ed82847de75938d23ed418bb9ff1caa03b5c1ac5d65692dd1defbc6013b3270c4314a45dc67883762fda5509b915e8277c1924 +B = -3a7141f54a0bcef68cbc3006166f7e15a5c2394892a428fa417a485981316a537cb3ec757d4a2473fdec2cd61010a9ff865852af8f43afc79a97d394bb6c58643858e2b4dc5cb958c33781b5c35aced7882e8b8d7b4e4249c2b82150adfb0c8f2bbb1cff3d2ea27ed24eae030ef468ae4d6b7462f0b072cd2a2f02426b3290b87b14d14b34e91a94c5bd69e9eda53335cdfa7df90a57f97f3d023ff85537fe0a8bc5d8fd7901722 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 34464b7a50713d17b01b5940b5acfaa7006aa6b9b083bc17e0535b08783761391eaca8703af2edbe13dd0fe9036d38aecfd9faae08c0861042ea1a25b41fa8a15b7721909783de3aca127e955e177987518dd010306a795bb66466fccd55bd9e2bde17470cbd36b1e8f8b63805229754387a5fb40f3ee9a8afb2e51e25c8bea +A = 701ae8c5bafab7f41c999e492f04a7626b2b1054e6dce1b83002b2d3de46717225b018733b0fa8fe3f973202da8a090ae3fd14f48b27097513ecd4ceb1b9729e7783c17fee9be5221fce4ed3860275b3b36b7416594d2b65e198ff564e82301cae23756c878494e57b5ea8fd22ad800a582cae32fbc985d122cbc6e0eac77c1000d3ede45ae7aa087534adfdea8e9f924efa1b19c43dfd3b7bc83d7c40df7c6578a320a19 +B = 18e0256543619a750384d30b6a7afbbcbdcd9a2ce644dbfc97a8ff699e118032558f706502c9b956695cb25a46d7526596b3d0b67b69611009265838bec533a9488d24583e7d7f2284e23c3cc4ccc5920fc57e24f60da0d479d41f5b9c6ad9152903a4f37842176c6257fb1e3e0681d6d583e704c1d1b24cf616fe638106638fe9d79a0c74f0df67cb2df9d99185324ebb037d01ba0066ba947d5345cd3201b19769d438c43292f572 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = bc57cbb3e1051d3a3035f77c2e375c7e3221dd472edb1a5ccaa7521849fc0ccc7568238aea9335a733d839e89ace6f2b66ef238267e0050c065c3d9553cf50cc5cd93d34fb43c3ea1c31b8ebf0b751f595a7e5e3e860b366229de4286b9d3f0267f78c6888ab3f208c55d9292079116ea0eb9f4ec2934c97149aa132c03336ea +A = 1ffb0aac11f6d1d257ef7aa997a030e2a12b0615fb11ff04f344f6ecd550e8e77e9883c246e009af33a51204e4066ed4249950e022a61337848dae17c88317e15ade5b5499c0d7597a69a02b6c18db0f975c19c16d2167c583571e947676ae9c15be60e69d76e78329aed5fa57dc5e616795b5487f3d52bfe74b54bbf93ceda093c2e14104a6d2f017f0d200a9fc89deaa283e04b0bd9015ec67598425312868eeefeae9c996 +B = -9de2d82e25b449b8ca4b02b2d2fc0a023fc5804ea553aa84674a815bd74193a2e549070e2cfa0b90a53070646875282fdf855940905f834f5a07f073093c658cd1813fc5cd7092af592092d789ab5481bfb14b6683139646cff8eb1c5dcdb6a33113d1c97d4b587f15f972c06046730b7e712a8e3dd5f4bfd07cfae289047de31776f222d11510ab6b70a200ceeb6802d6c33f913c509b31b96e2b8dba9e25b0d2250c3b102d814683f1 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9f7f4e010370ec1d76fa83f73c80825c3b71521855fca5db06d7ed830c910d0430375bf319671f6a83bf6b57d9d53cfaaed5bc5d615c5690df0067b18791c33cb9f0ac9fa5f0473e4f4eb7840b0b660962097606b3de5744089ffb37d9c0df1123a91a5896d4deeab8aebec469b099a3a9a4f6d822030ec2fc4d11636706fd0d +A = -7f56093243ec2399548ed95df79363e6ff09de211dfffc314b7cee526535def0f9a8eb9aa6f1736528ee7aae8be55c06645708d576111766ea33e0564c12103edd61ede3128a7a642f968eefd0d7f3768b1325c2dd910d459b15e54145a234225fd29932234e59d3ff5099ec4d5b5c6075f56382ade1101115c7b94e1e2a7bf075dec210fdaf2357c735416dd5d616335002d1cde6056bf7c478f810b78c661a3dbe6e54084bc9 +B = 4df1a6296428d06f51f31a1b0f66d0b77a04db3bb8e1b80d64da649899a1a55d4041bf0bb47d3e3936ee0f3740e1e8c2b235e1b8944d28c7d617d1f968abcde9dce10d6e3c27b2e3607d8df815f5a39da9b5569e95eee1fe5532c0a80011e7415800d8a9ec175fb1d13dad959becf04964b70dabde6d37072dc9f6d914309b850cda33a565515dd6c0181fc48bc7033b314ae0bd5872480e02ffc08dac4e3030d83b33488cf149e19b0021b +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 6da5fcea305cc6eb47fb17190889e6a39c339da1bea2d7c95e997fc538b4aeec8b0edf7c109faad7fb6c656420f4afa104ada7a0d3d14d3ef0fc6774b59aa2687c0b4efe7c3fc83194a89c832f7168346cadc2b1fa6fa9a23a67c91ad731b4cfb9943738c7f9951945b2eabb3743473d9c0444ade756291f53fc7641501597a2 +A = -19dfb98f9f7d20fd331ea749d2019d8367935fb75ecde45d6dabc815ab9e593e51178a72816f85aa678304e6ff3a2c24079a59aca253d76c4ac633fea1070753ce770765bce47428f8f5ae40c26a3ac91ddb551b3d575bad9a3b6fc7954acc93aad2131b78fd212fb0db7cca4195b41651a5311bbd4d8c64f1c93e6520eef8e6308e98caa1cd0d3c9b4041182cbfa131c4948257f1200b1c5351bee77ac8bc8e44680ce64ed0648f3 +B = -2736d5038c60553927f389c0650bb1355b0ce745a7dc5f52c9909039465344af910a5f6a9cc4ec130b9877c1cbb52fc08b20d672e42b853d26a02bc07eabb9e3f91399db8465b6a8b1c9f4a4b9eeeec6e9b6180f1a770c139c8f29ceced61cc7ba182884ae01d14dd85bc924391333e8ef039b586b6a0ae18db3570aa560c2b0226d5e23e7e753873637c25aeb19e74997da4f5d0755571785bebbc7dade57446e0df4cdb8df23c1003533f60a +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = c0265805aa8ab52da5aec06ef7cad2026fa0b18edb27b4903e3c068ca6464465e34d3f3bdb4bcc10a19441040deaf5569645f7e09b36c56631b3a6144d6206d39c9bcac53b54210db6d484cd6a2780bc68c07272de03a9bba7e51c9d86cc8883cd2e1864a2ed711d505930143c883c57545e9c40851c6df8b3314a8c9a0d201c +A = 5622f906b077d243521325be82a43fce321412bdab1f15e4ff0c11a7066a288b7939afc01d30243c8a4150e74286611ac1ca4daf457aa23508a7af869d2d55f54f2746afaec477cd7df0d5711dd636802ae7f673b3f730236ac3899330f89cb71d48c2838322fe856d9d8b4053d9c1e66acdb5e43614ecff954dbe37c5269d7ffe00b34e682c0be3d7cf653ef212daa3d55dff92b329126636e440b0bab55f4810a2849f77c39ebb93e +B = 1ebe0d1800b1fcfb67d7d54568e45dc604450c1dbe103ee21d48dda300c1d9b9415dcd9f5a56cf12c2ede3c862e895efb83621435377387b29b882b2acac78386895c7daa90810092bd3062a3a4867f92d54622d7f0b89b40fabc4709fd507d4002ca80de231596630c234fa418611ede0ae4a9616d570232c1b03329bad02220ef64e455c164aadc16190ce35b78060a6b117b4b0641fa64dd8e8cddb5914e7657573804e63dc7b216b1a9aa175c +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 606d2b6f756548568013bdaba6e811dbae88fb01f5f36d30d15dc1e099d86bdca9fc1eb3a785034ea14cb7f4776586327d57ca5a52ea1b30f26e2a76140bbb0e930c7780673770fe22c5ed443c349510e1494ebe402f2621b1e6bde39b8691edbe5c7242efaa6634553e6af146dd40666edf4a3db5d1e7f9347fa1189c1e5168 +A = 14ea5e6fd612945c71fdb17ec44d95015773edc908a85a6645a8eb823d11226545d05b81791401cefc81ce9765eacea7a619cb482f29d38988d355ce731bc9009969b7487a3acca2d2065c1faadc5d6dd8ca1dcd3f3d4ff61d0a75ef75272e62193618f6b802f70795041de26d6ce367ba996dfb91167cb1fa16c8977f982e1718de7d60275a7f66e4ad72ee55ea06267cc4e8b08f488579825cc674b0bdfd34a01bed08b62004fda15b7c +B = -8a542280f6c8bf4d9fbc96d5bfa6ee0d16a09dffdcbfeaa2dfa1097a760dec7bc540a0b5b2020bab1eaa594117a40a9bb99c3f16fc340c262b29909608740b8e77fe4706a88dc0fc3bcd47998e88fa02f617062393978ac1bfe14235d43f3d5edbdfb9f140412f4fc2dfc05a700f47b1f0f90da7ae07ae781d9ccdbb951f19a8b8a9a7dd8a65942842cf207f3baed3a0b2f08a06ad0d9ab7ad0110346293d51ec53ff8165b925c0e7906be8b7303252 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 512220042f151479a6a8b7c743ba83366cb7733caf37164e9c823422ccbf78b0b83f426a7230f559d50bb0ed3d9486c6a6e25f4cf96c4fdcb2c861566c6a73215b6d08995a14569710cf9e54abded1d77fc7722d06fda4557a3a99862e5ce963e1be25336fb42a4629391cde3aacd47ea5f5426e7185c5df27d9136a6df26f54 +A = -4d108217b778694931088bc255d1f69cf8f5a14252156163f948ae58d58f2ed54f518177d668e795474952c930052c1bcfcae11bcd15af168ec2e881e6ddc8de257d0cff90ff3ad409bb3a080d30fdfda99078cc3ad8302a4bdd77de66ac082b40fddb3cb36c75a86bacaf60984a74a0fd575d751ed2830650d85844aba9e3f781b2dc6b515bdb8d9459b083e1aa653ef177de76282e86c99e97dae9c0b050c9e6456a051e7d99adad7be4e4 +B = 7b9079504c635655a588ac360955fceb10cdea5f3de548ca2db681da38c17a70df5798f72cf18691d14a5f400ac69fbb47e64115cf071466c54bc7077a228249209542683ba57791352ef3409f6a947865d8f234ea9d39491b5c001685487b32130bce9aeade97d9537afe3f2f87e8f3315619ef7f215a73cb724f1adca99b90912aeecdc81485c0d00a74387ea99c965118fc6a9af1163e60d1ee6a1eeb12d7c2bb9a54f747a415beb5873d616fa0eafa +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = e36899d83a143c82e19e11494ba18478c0a9497fc89fd83df38adcb6b33918645a416626409a156899c6583ab9a4426438d9c32cac54b78df579cb7b6b1feb3f39ca4a6183743a4b823082896a89f9f1722be842cb2d2ceb605f84a9f9b61cdc7e184593fc2f9ff2994fe6cc4860d255809d04ab47e154eaec9ecc807ceb298 +A = -1422272d9e91a14b38b3e81cbd9411a0cafca23addf4f33c94a1bca70603db879dd8a9c0b95f5986bcb447731219c4f9b32a1e3253b027b7963ce40279dbf4008e526adc0bd7bcb2b533392a105c6e8e1bddfdd2bde7dfa0d2e3b1c6ffa07fea07ecdb9fc828283e93b0ce4861945562478b1a56de32251b7d31f9a2309488f7cbdcc38cd6b1c951570675ef0d61e1df69fed78979dc755f160d93ab5a3e65dc2944d3333cb85aaf87a153a90fa +B = -2424fc1e71286ce3be684a10dd885e4891b52e9009c3021d90ebcaf68b6db81130bdbb74869cbf142e0f44ae72684fc12c85abb5157987428c7812889beecfd7bb43fcac2eb6298ebf1dbcd2e70e4274841c2703b8685df18f6e5bbaa1422004797defc6ba843e77f891bbb46699a863bc1d77c5e3cab809c247e2975e8170da00fd9c8b232abc3fc6b16951ac4e6c96f9503c1ff2d6832ff9c35b2c8aa408645849c577d2b8599ef520da57fe2a9eccfcba6 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 4e8a59476d47ee2cd0217bae2981cf25a2c38e5f5d5c30c2d8bf95856a6e8f42429e565f1836365e550d85207246514624e7ed932d6f5802a50ff9f15d500dd84b27729c1717a3df0f2d6dfd40f0094208445193ba6500ba03fa3f4bdeaf9251aace8729b32ec3215bcfa170575e26265fe523cf44a071470e3b1547901e9227 +A = 452cfc78cb9597e67aacd4ec83e5b473ab8b7a1dcb6097fab37e25d5a6e25c69c73a6c20de0e2a744375bbfe7f612036e69c7a503255d9e17c6ec1dc6cc6f634d4c79bed4764496e5c7c026fdf9408242d3b234195e67a5681e7d7b861f58eb631ddb9aeeb0e5b3ff7a7657a7fde5975b8a9e1f643893bac47debf7918c7ef8f6d7439320dccaf63b80ec9761559078baa8e35d98fb9dc242ba83536eef7ba9901395ef02b19990d8312203df7dc1 +B = 1dc222e7a737e6d97a703fa232defc6c0a4fb2bafd247c8e547b9c474421cacb7692ec98f94be19a5e40269e1f5713d06a6d081a943dbc667bc867e481b99c55e437061cd44c4482649faf870d9347e0252ba9dbe116fb4992dc2c2a0583c1351e9e01e71e9324f5fa942322485bca93c2d95cf304028e68224fed446966073ec7326c93ae326a7a533a36e053437910418bf1761abd9c4c5ab7e6f538e9bf963903e6c80f21a0a38a683e8166e4626a8d8b743f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = a4d5e9fb7f0d75ce41ffecacd2ee1e4d15f82dfd4decf5ab1bee75fb97792d0d574fee60a30b15af80bd38e6a25b1821e61628dbe456e39fea3f8a9ee6ef3d2332412be1500fada0c1728a1457656eb3e9d94c64fb2d0ac89f10f2b9ff57d73207274ae7e8c7538936cb7241615b830cc9011d4363ef88f51c7b3ed503c25179 +A = 13eeef030b3110451fcb1a258434aeb51d3dc805b38c72ef7c79d4b0e18d600e5dd28b552b59f3dda1898367ec7da5dc6d9089a585cf52002eaf8f9ec64b8d3ec50d0bef7dc3faf203c48583ec89757cfeaf888ec4a91470a6b8ec9f26a6b07f3311b4fe972cac2f2ffe47f5c11d2dca87c62680e2229120cba4de9cfce9f7f5c33af8398c07ffabac1675de1845e05a32536329647214e54e5d9216fc0cbf2730898eae19e425688bf184d16bd1d655 +B = -ea324da99252edb03f40100e528d9a5080c43be97fe4b7e03d9563ba48040d328e57d0defd4b7ffa9bef3ca0d2682aefd2a0ffca8566e755b11f2e3c6c1b707f1b9465592aba6181e583babd5c70588e7123361a8ae77d8c398e33f894ee288babea1d7eb63e2f3de469e502b5048417043c5a9a9a3eb921cea1533162e3ce9c79e6caf62bbe7e17b180b72c59b9ef5fe1a001b733d909a8278029fb4a63077ef9b3545f1159ad73dd75030aad599ea4884677e01f +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 2f096fb8fe2156c41ab695956f13f0fd9a084f87ea5f5b1acb6b60c62617b8d7079f4b072223ba18cde474af3942599fe070ddb0ac1a99f42b9506a2648e1b8f6106015aba0bf7a824842403bd3f4ac8b6fc4a9861bf0e8ac59be0322f0495e4b515fd579dfef273160ddf96e453f4ab663e703609c709fb1f016ca919fb26c +A = -4212bf679cc00adb2ca502604b71dd5dab99cdfaf55ae92aee6bcf8b3b6354a384656c09eec6175a95c8cb4591ce118e783d6344525c25e5b356e45802ea3ce1fe764833132e6b7bec434e4481c9cc2986904988bd8da7dc2e31cdc481fd0e359674bbff524124bab1ba4379885a6cfc1b73d953e6d1aa1b938129d74fac9dc597c31383f2f7e02fd995f7065290a9812ba8e205316ad5bac6fc65c6c7310f1a6b033503ebfe85bf6d3851bea1b65b9c15 +B = 7ad83f97f40d5be508cb394c128764532f0aee9a108eb02840ca1c635860b6d751d5f676e8670e2f61466397e1bc68f97ea52d64b335d07aed22f20bb1ed19e3e42e4205d650e6d37714c2f80d39b111577725e3bc7ce75bd7ed5e44f8377d5fc2b97f05c3c1ed5ca1ec90ba3ff7935a25a8acbcb15fe1fc7aeaa1e444cc2f06c1e6711721d24b8969d465e4958cb87924b3e0fe99ccb371009b5b15747bf6dd5d0fb73b8fdf58d955c8773a55424a34c741406f6f904 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 909626a69c803e9acdca97c56781eb672d6fb31430a53b853f467ca26d4ae96c182d71c0212894b776c88e773acbe9602e3ca56584c39b5947724290def7dbf04c6853a108c1282def95dbd5bdc015b68daeea0ee959b35bc5af98a4ae4cc7486e627bc9432bd009b21ee9af3085f074a3ae1bca879e321018e991e7898f2897 +A = -14eb8e28dd04a159c576eb10578c24fad9eedd3d8b7560b681002a54a4bce2167de05cd061338f63c50b86327a79595a2dbfc1d3f4e76aabaf88cfedb69faf5148c61f8cfb2130511a3bf4a17d846ededd4c08f3b635182dff1854e8c4c48007af028e06f01235fc2becdb32adcb9e2058dcf8f8655624bed9915faa06be972282cfbf8530bc0cf2de5b2057df32e4a6cbc3c772feea0a511cfe3408a6dab0e2714fc4cf15602ba0da03bf0016f1f3f5ddfe1 +B = -388da160568aef9f82fc16f48a22e8d7aeac99121cfac9b748c815e5d3a823b673ddcd20c1168f98ba204df5e52535f61b224fc0374092f8c834321949fa0a812b5e65c492fd9fe8246b74143a943bcdbeba16024e311d673357a3dd3eaef9ae3a72bb06e03e34e091cbe5b6a9eb9fa3d7f36c03baa5c3e242f2c186b58db5dddbd73f6aa54aae027529b8f8f0a536b9b283ab08247b9977a2ac2d0d9f162ad03a2fe247d2c589b1a2d14b5f90d5b9c0a95918ea956e261b +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 128e8844a2f04704a4a60cd33e85cb7ad373ff683abd167894a35a1daf947f504c0abd7a614e293ce10797a5330147c88c4d5e1dad1bdbeaf74095e3f5a515f2af68b7bc11ee1f53b493133905b654318dcfe73118ef1931eac47deb6c4958406b704ce027d9b027803eb8e639b52d5983094b8ff4b54e86a7dc6ea169ff1af4 +A = 75e6b045aa44dd9b8f4b434dd4bb1346fcf558a5e96b00fef9b6cfaca72fe8b1672edc2a64beee8b959683b1861138b297629b44a0caec6bad2ac05665728379cffaf66a129f0ba40aab7c6b1c3fbdabaabc87ed3dd580ba80ec7ee765e9a8fbe845c0d207eee7a1a3a0c39650c75ccb6bcdae2e0d5149991dc3bf899ae9b7626a2baa17b168b260d82fba84a12f10e09234035e08b730cfc230f0d2651c03e34d4952fca6409b5c6ea5d8791c90466bdc4adf2 +B = 102fc193633b0e60a48dcc17aa76f3e52cbbd1012f179736a0ba7a102f8dfadaf434063b0ed1b1528a018b349eaf192fe62f868b538cddd7e8e6fd98b93147727d58561517b2836e4a373bb31fc8d5e42d16126ed80b880c1a37940c138fc1f7255ee0b7fd39b1b799c34e5178580cdc076ef3fbff65fdff7497398fb1cac75e5c09cc7df1168a20f88a16e7b3ac78091a90f1169bccd48c0d06b4707ab79b741a168deae5ced5d48bb5f5dd3f465e43c82b9db7edab24569b2 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9aa9699d1e5d2c6acb21e31890c1899f30a925b834adb5b8bc8cce83a1718944a2c90faa71b34379a21340457478c0c43121dbd65d62e290eda2ba6230bce4e6f18555a1380c7c95c1700793157f7c1cbabeb09460ca28dc596bb17851ab2ba6dc6bf311ea69bdb7fa8eb78df74adf171d4677a154b8536f8104d919bdd58648 +A = 157fb9e1b38f288db78a1a0e22fdd9f48a59779487a9ada2774a094d34536b85993e7b9ab6e24f081c4cdfb64a82271100a054169e4f1c24e3957ae9aa8300e85eb2a45a6d5987eed4f0fba6fe8557cbf6128e018c5f9df028131bbba6c544b2c6312aeddc71405f0e4ce648fbab9e5d51685949408e4ccbe06fe501a36fc13ee65c31f062313135054b7679eef45964c77f5a1556ac09b11c496d0ba8c6057e283bdaebb4e6d9e5c557d975745f9f98a288d5bbe4 +B = -82cb6334479bd997c771e894cac1ead87dcbaf8f5006be5c70ad48ef94303137bdc45f261af91a201b276a17d884a56ff27af7dc06cc5b7b9c94f7c4d4a36f68f8d309c477b4969a6e7cd1b2afab9deec06555cb753d8a0eb00965359ef865a84bfa87b815a42b2050e1635d5ae5e3743c007bd79e820aa37a968702a960fafbddecebe63f022553cadd7a4d4fb27b4dcb981e8b490e80bbbf13af8c4412d158775db71f5fbc9986e7b8a8f9299574abf7bdf9ce7544e8c4e85bc +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 46e401989fbcde9d830dc6e3c42768999f153d44d270d4805c5beefb470bc1e82706aa7173b359763c5e15d146eca91a32a36f0a80802871933cc7f2ed15a5472988849a2d2f57543345b531538db57ab9bcbfbe787efb0a82e61baa505aad628df5f9e881dababb35bc2decff267eaed3d3671757ae1764ec5163b792b4db3a +A = -590c16ea2cf7fa7f63b5cf74804333f22fd2d0e1da7d226da8425abad2b39a4672fcebcf5cc15d220b0ecfeec09665e682fff0140f16889f7a6ade9ec11aae3fa3a369b3fc133babe52e42b7a8bb9a24777521f4d9e0efe7d7977dced9e40784c24d2c6056b3b668ada7856da71af73d2dd33d2e481ddf40999d86a6e236d0d73f31a67c52cc8b38203bb2840c0b92c2612ffe5fdb6be87f9a787d70b3dd506f9a63d144db3417495f0a48523c812d14a89710d95bc6 +B = 5a2865cf2254710a1a51ee3056b0c1f6c5f77d22d7aa8f939e6f48ecec529a169e630c554bbe682a8c4de9ce4daca77a278d7e752cb678141ddefa75ba42e661885a82ab55d699414ffeb75802cb8f4e7583bec8a7ab58803b378bb60fd46f476ea490c9aaba568ec17f3a6afdd6f20ec54a512f7aaf62d2f941e35b4b72dea77095e863dcb38bcaf8777707c1dd437ef2ac6b6a8b2b832f80ad2a6d6f279c053d02058b1a657a1cf5b6b269e15d29087b0cfc0c2d4c3fbf32a167a3 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 1c9649f4540556ae82ffd71b2c71ea8588aeb845c50dab595db9f8faa01a26c809d30d8433b6c0add465e164cda2b6723c942ee87241eb7baf9944cae08babd8e22a0eaf35c09e9efdfb9f8bfa65d53ee6eb23fcbe1d12a66ae05e7592ed788b231b000f895d098a24febcfa4372d249575926a5faf966072f29a62a401ec51c +A = -1bc9ae5fc2f6a3f1274584bac1e145f02c5e8c4779f4df15e98dd34344c988c1437ee4428485a09090d81b18606a6ea5c1b9136872ab5b37373fbffbb5b3fa8fbeca1e112b9f1643658c2f38b9548cd8f0f271779ce0acad403177057ea0a2af2e7435109879941fbf463488a2522b831b95c1cff21d2d816d70c25156369dbcf04a0e28e1d746afb8a77713703fefa512816fe73e203bb4c3428efe09b946b750199bd7a03d30feb90230c219a103ad4528cbe0de1e5f6 +B = -39cae179d955049f830867d4115d3bae25127c945b1fa0c16fa850e8fd77c1b3b9b7916b9983c1659b7cee77b7dc72abfff1c56681b7931c5e58cfe4f1bf0168ae32df0df8f652223885717a98f858a497b1a4be62a2215c39316c34451b0d957791f49139921d9ac8041899b8fdd5d3d443547a26ddf5748147e4c3e93f5043ede42f38a9baa628df65d3d6148ac2ce182056700f0f94029be05d3ea3a218b40f65a87b4baf097fce107c080de24880259f1046175db1297016af76d94 +M = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4 + +ModMul = 9fcf6a47addfa336557749821a88ccd2573a5ce2c3094a17d9a29b33e043bea165499e89fd2c939f17a670694aff05e9af46836b62c96e597c83681092d63ab9d6e22751aa8fd4b9ea94a90a373876ef0f6514304a495edb5ca1795c9ade7965c70f9aa92f8ea460ccb670e9a62c81e9c +A = 71b93fbad39b1c2755f2051ff7d532d59c985756410d58aed3947d6ae737ace5aadc35e7e0d29c684b9d4bec9c0fa277996bb30230f70431cb7b905 +B = 167be8381a3392dd4df62e150025e13b388bf366922ba8632614928922cc290772135857d1b5234d51c27862cb1a055c1b86260b6ec +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 8e2ba940fc5165c6c5f7f4cb55cd89d1d5f59e90e78730bd66fb120a814514784879dc43ad4f355030ddb3486a59bc34b601474978a94ddbceafdc0ee23cb18708bdbd824d37cc32577802ac6057fef29a71f168e816309fc80cc46f251e7289c6a57fd222d5868263360af63dd73e7c8b1dd6b3f3b6939849580b9231940a4d +A = 1220ac4bde4feca135268550ddc79d8b05ff72f483b39f77436f348c4f5360c22c598f7dfb76697bf6d2ae86c68e90748b8b729b25f932b2e5fd33f3b5 +B = -bfee56cd412318cd62e7b6cc49217345d3a94e7fbf6fa19053fa685efbc0f8b320b7e43883189396781c49371dffe7d126c032d1ae4b6 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 8e2ba940fc5165c6c5f7bcac0e449b64801e75134a390f120acc58cbee43888f50d07f7aa6dc2b33643c025cf745434d20eb1aeda8fcee5fa3fa5baf10d67c21390297857aa50bbcc4a29a6b10885f97fea60f1b88fc72512c111b938142ee8d67545efe386622162e8fd50418b09769b8c22efe54fdacd652580d609f0528bf +A = -7bc53f6f2e78628678ebc8e35ae4905caeec61acca5c64fdf595689cf005bde2265cd43172802fc133dafd933d7b48def44256868d202727a4aa6c0cde66 +B = 74147c93e729707111d0d531b1c135453f3e59f63a7e082b43dceb8b16cc5debdb6d7c0ce0c00ec9b5ca51e7673e411c3cab34938124db6a +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 43c47d7e319c32a758360dd726a1d91e2cf5c57f73cdf9ad2040e61a9c282a2962d96d300e04288461eb1ed37df19e6b88f104a250f9885898740f6487b081515314e0a217df2d4345d3cf81eabb2bfb346b634b9c251624748f6e9407cb677aff4c53fcf42cc027de267e6ec011e14bc7f3bc6666f693d21 +A = -1e6ce0b44105047d0da0eca7b936980267db41d41319dd5315889fe8fa2329023d7cf54f71ee179b5bfedf442cdad1920d311966f7175cbb953bb42ee105393 +B = -23a330c7e06cdef4b6b121d15a9c0bc774eb5e432e72d04c5f03a0c588e55e010b61f57c03c51edb1211685d8dfd2a35393091fd0e3ad2304fb +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 768293c84c431b9c8dc6e538ca3f856c60ae5e1aaf42325865418b7bed16c7fc2589968319cf41cb370657c8edc7b969de10e0566b64ec796470b630e22477e7aafb38e99b6012f100c9d23d5517d486e3cab1fc60c1568c0228c9b55d2d77d23b1351fe37ad4fbf9c07f29330a539de4a32709d043dfc9e21aa1a +A = 6bbaeec78b6a41818b7eec42fa3be7d639dfd86fbace2bc14e0369dba6dd3f04ede8b808743d809f43f70f1146dfdb1d649546441919e27f1f7a9760da4a3b152 +B = 1199dc2f52868a0cf440f6666b576541c7aec1e9cee14c1d22010ab0f53fe8bbf3029c639ff78d89dce82de85fd8eda4e67395d435df60158623c5 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 8e2b90afbdafa02ce68d537ae807b4e7f3e05a66b20b84cff309941fc3150f99d083841ddaf6f19f5a76886ad5d853c73051a0457e95eeb0fe3776a084a027ee77d14f3825713a59622ea163a679cff904db33bf6ab23b06eb4b31f4e34fb122c8c170321164439db783e7bec1c265eed33f33bd9cb6d1611c00aa18a9b4b90d +A = 1c4821515167f7073d4b7cfa318ead1da1131499c12497447846caa84176a9d4af576fe549fd8b0f77bf8dbebf6c395f84dffd40400101bf28b1dda0bbdcc5da255e +B = -de60cd639044e863c6a49c73213dbc2ca84e4225aefa5f880e829f2d9cb48ae92e3f2680c462ac697dc34da38f65fcdc1b4d8c3c99e8cbe29660b539 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 33e8e8e193b4b99d8bb382c29c1fc5403190d7654f43cd77e28d1bf77bc3a728dde9de9a89c6522ebc7222d25f46833fd1753a44275b04485c77b675d816090280b3541ca61bfa33921a79f7286830131d6eba13acc46cc2c449b3a359f1cb49d67a4d0cc1245f3f8b59b1684aa0c3ff1c928b8e880a3375ed811dffc991fd1d +A = -50ff3e00feeb2efc6df6387d6409a622b7a8297a717b8d94d0dc41c6ec6f29a8455c3580019349660b31dea1e4f66b74147de93535e671c853b604ba06a9b62d34646c +B = 49ff858c7081392defc3ba12ea8869fd61188ff15d9339be72657b00530b851de53b1fcbe16034816e73251fe1ec97bcecd8bccc470373974287ca328af +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2c88dc40414969e8b614bf8db05fbc38fb2b7ce144d7e707f9f8eca40ae2309c1fc67e713a8da5fbb20e808ad20aeb369cb72a77fd285e38a7895ec0fc795ade4ef1f1680f3a3b3cee4569cc9d5e699984daab3385815d2e515ba5d67d21dd1defc12ca81bc8ea645f8f8d103b4a0a9cdc92eb50690c07a037df274bbd5217e4 +A = -167ee0fa8e5d8b569d7848b068df06f6baed80f6fa6a442f9d11d9712622b512249b92c7ccb821ac751fe4ec0a7a47e04ea5571c7cb45a7985749ecdd87f0c0faea01d232 +B = -2207fd8dbf2b8e9a5e3cc515479cde241dd3671803f9fbf7859459ac66705be055fa759c85631ed2a61139657eee7eb08fd963b49e33666e60b7e75dd26b5d +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 674885ca3ef617a53eaedb9564cf96bcde131760ac541a81f4b25c174a6fe1444c2c206f7171e343e1bb43f81610162994c497419e75aaa25b664c122ed2b27640b45bf646fc5da1703fbf1cc66e10a3c306eb69ae5f937081a1a18dfc8db376ea18f4c1c499109b0cf8806eb32cb1f28985da790047bd7b32c1f67bffb9761 +A = 413cbcbbb5851a4ae12555801f7f80ccd888bb82ef1b5c31b99e1901d7e0ab91ee489c84044bc21fa2010f11aac21d0531fac09feb482fda579cb9f224c3149dd6249b0225a +B = 1b6bfea70f1d80350eeb45f9a5cebda954d72cf5cd27a299ef5a42e1ed0b50a541d1657b70e50b0cab69b22e31d0944fd735957b1ff764865d9385af302bb802b +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 8d74ba5fdc67733ced4d468f6eb6ec4c1ebd79c97682c1d4daa06105788ed9c5144992e555d903804d7ed0dd9b29ef2648568ab7ff462a03e0bceb5482485afc3b91448fcfeba435dc587db6f3a022428d37fa0e85392d0e48e7d4ed6b21253084e653da8175587b3b709e28426cddfec8d9dc582d4ac2f3d540305c0fe17327 +A = 17c0b7f0e2cdf316e4d32f040e26d41dbde1e6689d98f0652da1c380daf5dfeb6a511b72d82f1b32d3852e9aa2f594be10776a8fc89a8a35c160e8e41b42a06a342fa1c309fd82 +B = -d7b7701340c5a358455ca5fa314ad83860d9f765978ff652d7f542de2e123bb976930b8fe84b9608648324450d8ed2bac4e44f2fc71711ae813cd8793af8d3796e8 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 57e60f79b4e156ccec4c253e70df8d86e4aef326150d612a5ac4dc285761e88ede412d28d9dfa5a6f5c073d3c91a65ba9c86067d81f296935f0d0ebd2af82e7f6b5b336422429cc3b8427fd8d3f5a6fe936f4208362632093bdd3cec1aa8f4b176d260f605caf4a12cc011f3d1b76135ac2507346674e41673eb16c0f55d8010 +A = -4f1568c207a9ec970b5c26f068f3cc8019e8cb483525d251cd2919b368d072ac8f40017a19fc7437cf88e927c9e7d6f539ee84865f0af24be0d6d98fb33d74e3e0d28020c00bcd61 +B = 723db98a78f42aa45496f31cf78695583526d25e167da48ec310e447ad3540be2636813a2c2f7b8c622795ac451992e91bb8e43e5737f0dd95623282e729d815b08ed8 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 237eb5726e2c628a515104bafd44348dbf099569815784eca5d6a415d3c12421c8c70fee23d6d82f7b5b136b70ffed3b6d9e98cb47854e79239d96c26f2ec955e4ea8dabc29a1b0765c9b7af6ef09ca673d1ee21c680e4b8cfebf47bbc74c993d017ead6cb6f3319ce4de9e9765cdb3ed8fcc57a1b153327e1a6a965e5dfa89 +A = -1fd1f634685eb1470dd9080529a891253a28a0b31e15c662733e20d43fc4cd71f4cfe83c3774adf8293a0fc3bd806d0b31b61c6ed0b4414ccdb91e2994e22797e5771c63defcc0887f1 +B = -3ec0478afdf54c949a097ca411be41f931acb750ef4f0ce97d0f0fc77cf15970cfbe24b170aa332de04836b7a0e6c5d456814182d27c8310d5fb662a818bc421587d95fc5 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2f1d500443fc4f4b86e7ec93e4d0dfd3faabda35a6dd31445021928373be14c37fec369ce80ebcb77aff2151b7ea94d21592da1823ebfa0af196f286d7a69ea54799573bdcd4d09ca4f33b8a3a93b35de5ff7f65099d59367914f1c79440b471ced6773b0802bd8ca99cf531b62892eb1e78d67f8210592208859b0aa1754b14 +A = 572de2984fe2ed0d5ebb5bc3f62b197fd592795d91cb16b48a0c898991ee3e884e5870b92405f248036ef9b3898c5ee6100a09ede5a48bf7edf3a067e4fc77e7e6bf6a6e3d4f538e3d66f +B = 12c379402b18a34dc8b80c0dcd25be16c99d6f76d5d64b6050b90910cce594bc022794640735710c7ded857ebd44fe5b2e51574a2296f7d7a61b59c0123051bf2ba4a168cf8f +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 4001c734e1391a88640007893f167eb79ef61e4717d5eb14b8d80c25ed59c753be63fc8e54bdaded22c9c7d3e49753eb49efa010439807dba0d90ec4f9b498aa97f109af542bb41922936223213ddedac4d0fad8f1446498f4228b758aafdf1d9692f59029c76ca2832125ba50e811cb95f2b982a7a4d87b4726e6dd8b1963fe +A = 16792909716b581a936287d0a8550a1f3e840935f0f3ddca75aa32e3489269b078fd19a16f8d6b2326eebaf46da76e90890c0ead3b35689bfda8c1ead17a4f672588f982cfd3da2c2b9bdad9 +B = -95ab2c47f85001aa852d6999f29644a6a55f9e4e12bf905f911f90d29cd1e4fa4fc9d1a2aa6c215bcb5c5643561499aab8f2678fdc5fa9c6ec138aeb2d62f635c45f239e46b0fa +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 1bfad44b58d3f8bc987116d4cc7ac98f89f838a8712d81d726189e9e1469cf46fe04675dc0b82e6e556b02c350ef4e30ec6203c7f1df937ea80f435af7c10f48538fe7755ba78993f304e64ca0d783b0f46f61bd14fd3fd30768f233c59018ce911a94b495f58eb96438e416ca3c7eba5b1bca9dea5a770c1d2d9f2f62f821e5 +A = -78a6a6ef40e443c52036e75f0b35938d632bd45aebf45a1fff5c2e1b6f601a57382b9a82c3e8b2984e643eb1570cd83f3a6be6daac567ddf9f37bd96785662bc3cfee6f47503d239c77781a8df +B = 4920f870cf9f371050e64a419ebe07ac92dd3525b41e8ecf6939a267e1ba853d54862dfc95dd21b3526eb0a0a7a7f8fb67df2e9472dbec81e15cb13266257177c5f2b92fced4cea5d +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6b0b84505907a5ca37abeff9a5ba169975792c69b5751d9845c0f09dea833fb679c8dfbf3895bc470529e0cc736c9b4a0d08b75d709a1d04525ae583c5ba082d3bca1355055c7bb674aa1b92689cfdec4dbac84a96e81c855280e417f60e7e4931ef4f428420c0b85d2cd11c1030a47788d6ee6af0a76b5364fcf23b270e9d4f +A = -143d843e3b12431fa0d873815a757a214cf731c298db61ab13cb87fe78b0a6184bd1fdcfec0c7661b10775b4ee2c815dede0ed497977c9ec5154f7b24a8a786501ddb8dd257bea51b9fd9401ff760 +B = -25d4da7b64f439987eacbde66abadf0da7c1653c1c1c6d9b2092351fbc714a20d2d7ad8093209da371150b69b3602480595533ecc1f3c5005a8ead10732272246d8cdfbab87c49e65223 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6bce40524278ce242b0b5292d27751a3dc414f962d9c1cacb45fa3ee693ac6890d2ff1647abe578c40ea8d4b326a2e0e2fa7cdec28fe2da089338b5fed91c4277cc5be37537eec2f17edbf48a45fbe38f15c58c3e733d408d001262dbd40c9d246c323e7978df4fb7207aa9270a12921743cee2a483e7e71b221b09a6b2c667a +A = 402671b0cfe14655bc650bd35dd0c36ce7f65de274a0cc4b708c6f6c3e84c2125ab2430e702421904950b29aa8a03b049910305127890457cd0cc97a3e05df67f29d28b0452969986959df02f59d207 +B = 1648c29205f19fe4c646eb62e8ae9b65260c2cb8424a526423c6bc04ed55870cefef9b8ba808f8ed2e1ab170e2e411f68b934abb1a22776969f79f9420f8bcbef28417582942e26646af60a +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 40db38dcdc201648da555f1062bbbb92c632c29b66902eabf90d98dec69ab3f3b28e60cad1571e7246f4c9e6aa62ad26a6d0bc08598c7a8571fa830cae4c2875c5c95a59f3295f998681edba7749b7e38cbece8887a7823b4752165e1a897e638836d408f439f009d0fb6c196e83e83ca3289d2bd0f0eb36b721331e4f9f80fd +A = 14361ace8ec5223bf0165b78913b77ef921b7089bb5e28891d120bd3db6513ddc90404a4e6cd027f9b51fbc02e80d376d59e1f2b043954199ef8218bf26cacdc5e749f668ad3b4ab35cd796f94c06307e6 +B = -851a39d8b0101fdb22ea9e367286e572dd132b8a77a6a14dd0e995131467aee898230f37dc6224e35bed2eaf459aae579181a161450bd7ebe6b62ea7154a8a0ab590ca4a6c2f05531c4e24650 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 4b085796665458b798f824d1c1a88c23ecca456fb88713b433228ca8735141a616633ccec4bc53ea4f6e0c74e4aab6fece2e4cc4c4efb479638cf54caf55d4addf75908076f5fb487ed00d540e5b984acb8f81cae3ef51db926a06382a288092b352793de721c23c371fd0ce7a789486b2e8b867d35f47b5daac2d339d22dbde +A = -511565611538828ff7dbc45c273fe46f4f5105d41ccf5dd343b41e9dc579429e56a9cefc54657ef0422960d1375b72411a5cc93ffa323455e006e242580358d6cfb641f46b9c36fa777a613b17dd4a187454 +B = 4f22597947638b9a9e9b9b7c2a8d37f77259f1bb1c7db65003b6e1a1c807469c84c89a75b80bbe0324fc3aeefaedc6ad9c0d9e470dac9c30bc48f6abbbdce9547ad7624f0ce9ff3cb6be23e47bc7 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2b90a57349ea94ea818207fe15c164f9d3530c7cdffcae178557274552f79c4ab56acd78033a570bd6c3e45789704ef0b0ef586594fe4cae3ccfbf9ceef46e769589b084adcee3ef8345375b7103232465b991273df724964248737d5eccbac558e35e4190112571d3e7c291baa7aa8b1800121bd573b8419f627c0091e1bba8 +A = -170cc62ad57094d307ce1b317ae5e825c2f2e317ad6060437afa105501caea00dc9a86af8729e2f3c3a854387dc3ba368c0a84aab1a527ab34fe27b0a69bc71c728cca87be728457c65eea7d7538ef3aa282615 +B = -3d9da1377a88f647de57ade46dc7caf71b4f42bbfaa5e77f16cfcc90f00b5d3e9e9d82355104c7cd0db4c1dac0496be3aa35706cfc0a30a1329755faa439694e8e9b41fba8f1ebb46140818c7008e27 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 4cd4da762c7576d582572d3427abc4b4297f740705fc14a32b46347541b152d0d1e3a11f27213badcea1e2009e34a63350c7a59e4d43654b28298d2757d6b54c4d82f580e98de4230cd119ba350416452cd4b8adff29b9f35ae0c533f666cfed716838e2b91941dfbea8d6a978a369d5f27554ef411f15e5a89850655d7f3f5a +A = 4f4a28af27b926d8ac347503d6ac0bfec388a6c0b38a577501c3ca4aa709c69601824ddeb5eba4d9e437a97f3e4477e1487d5ce7b4a35b90fb863657a5b2d901bb8c3c838db40b89b495ee9875e8eee607d7b8013 +B = 13ca192603bc8b2da29dae67159e4f8d32f351a503434ed9e4e24f74abb5908ef7da80781c71b1a5ce64fefd13a16cc1eab05a370bfba2a97e6cf90cfe98d3a487ba72dde0762c36c10e1da175f1c1b5fc +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3812e9e835ae355fdf328b29ed8b86dc3f6895e379b8b5d65a5de41eab5fb20ad3e2290c8ca69f9500248ff883d9715f59d0db6257d13c5cd612211bb1fb99867161daffc77968bdffc1fe48bcde0fcce02ca93975b3cd9e93b56974ab4beb59582c3d0ef2a65957f701549f8bf858de0c5bc98af3e5722f1450de391876a2d9 +A = 14ca6101af00d67139b985ac9f149accc260336237dd2dee802b5cc6e506e217b74c1a007ec10c20012f071ddad34e7407012669109ec1f385566ff04cf1a1ab7562353c0af1ba1be0baaef920a188c60db27970f64d +B = -94b683326e9de19e414f653aeb2cb4bd7b17e76a23de6a4d91c43d717a35e08f2155b444a9549dfd01a8aec4dc901ea9f629f16bafd2c84828b12d2f63dc154323eb2d54938895ec4c9efbcaaede274fd4ab +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 5ad7411cef0581b2e675d03b0ecb9969102a283eba5e779bdcbb7646d94e843083a07269c932d18b973b57abe54eaaad0aa76cf7b61f30505a263bc95aa063efb264ae829eb1d1d5f7d380a0b4db59839de9ae6230ba51901e71b3e3d59e8c34a79678e751c8b7ab139123bdb2f04d90a18ed81d2046ae86da1a73c8dae4fc4f +A = -469f61cbff01f0e4124ba69a860ec6dbc75cd758dd8ac7cbfed97645b16488a329adee62d1a66e90ee4212569d56d58b61676262f49dcb68296bbe5d8e23853e3fefe8a304710cea568ca65c183531a992ec5b4d82e226 +B = 4a0d48e31cb8c24a3b2c9c95fd19edbe46823032ef4c97fe65d0a30d5c2cad7a4fbbe89e0ebc9940ed9f9ccb8ab18bac269759a9740a7985809d0f38259e680f0703febe7fa012d1ded47f0cace4a133f59a721 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2b2953981db406ebc544c39dfeb08a8b089064533221536c7fa2bf2a7a0d3a1192859b7dc0ea5036eeab5aa371e3e0070c3980433adb3e3a5202ff257bb546bcb9550423201a35501fd717ed4c0016eb3a675ed399340bac7f058a04e69c1774590fe747ffb9c27e78ba50fcee30ce533a1659fc49dc080a60f21357a6265d24 +A = -122621d97f42b65b060c84df3f0c0da097b5e240731b77a37bb9471e7e398b242db6f1b5e25062a9bed702860ccf6aaf386c1d6fcf60fc31b8c190d3486949c5772b9e621b863a7cbf29449ddd68b7e0c21e669492e58e94a +B = -33978406dd30ec2b192c416e422428683deac210017cac9e4355e8446d6969295b0fbaa8cabc92c1fc0068da70efa047f938a419bac160ed6f794a9f69f53a88648c9725610d5f309b652f5462bd3011cf68ea859b +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2104dfef151526e072c09a4a277eb981a035379de3b1a55a88cb060681706f26131c388f5572c5646826b119c85ed450207f32733487e3c4e1e9d701a65058c4b4ef0cd1db090495643038229ed177b54695ac32110619038f1c1cece14faa693d88476e3d70329b0084d0ba5d547bbaa5b59ba1ce1fad5aa2f1c11a75bc7c0 +A = 7b79e6f1330fefffaf8521089c3348593e40ab7e8d4da3d4346571b43b12740958336580afd13619be3dc2d42eefd9e30599405da3e32e7f3a5655ece8b77a367059668021aa092460de75e627526da08e6206b0f8f539ef40e +B = 156e234931907c0c0970c1fe6bd4b24225ed94d5f5b1be4693c8e141e9a6032425b4a47b6eac6265afbeb9d796eb230efa707d5ac4a73808225181cf814b319142e9d175ac461c75e6d479bb6bea53954bb981062eb16 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2a392c5fc96c29df2f5ae9eaf76e7d981dc1e2f3b47b43a98eaf556a9465ae8727c622188123c64658053ec50c25e54ac5c6c8bc279b134d326e911f14c873357647866eccb4f9038ed0cef5082c2058ebd71e1619f7c8f8f2fb80871ebbca3fbfb7845bd855d307d2efd853f1bfd467fbe030862f165e53a9cfa633d0d3fa23 +A = 1e0430e7cf15173d00592037e83e717c90d7dab4f54a5b2f0f5772762fb5f56bc0b2a53ec1bc3b960afc35e7b043f9d85d0af6c29288486af3e186e52bae6300b58917647231b40a12648cc8c020a797683a9bd7ff34eb6d41b928 +B = -e08372fc766eba6e0ef55a9149d700b503e2e3f978c8a397912e2735d5bcff69c461561ac0822c44160c7c1bbf722df421b74beada57462ac54a9bdcdb42d6a27b86413036ed2282abf62800fb2518a32a4a135bc948053 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2608f68632ef14dc3979725c8cf1a0db10a1651f17d91247edfae9935b53f6364d233b030eb99871a87b7bd876ab2cfd5a643387a7af9d337e81770db04a14f4f8dbda2cff604838c9af9a31e8dccf9277d453176589ba33abf77855b9501e63370b2e6cd22831e1e70ff1815302c0a026c70042957d08e74dfaff940a91a7b9 +A = -5d3568858c05a15bc9777af949eb01d33dfdba58439fb3f7af2ba792efe8e78b16d7fbc2a303a4c4c4be7c9d43f57405e88be54d6ab55268a4739945ef582921d2877019659dadbc76e0939f4b2cfbc91e5356ba2ed531526ed5b9b3 +B = 47f81f65ea1af04f702757c02a175a299b23cd8ad551fdb67020c50cbb4110b5371dc5790b12484e9ce647eeb24c0220a5e62aaec3461a9dcdaf1a22814b6f22d66372cc5ee31944bef33469f905458c172ec7871d9dc9c301 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 5735109bd21d31b5f54e9221bbed78c54cf387e39c13d31557e8173e173f786b2d2f1acf3966c3bf4552fe9bc802d0868a5a7632404cb91609a7a45fe0fb83fea8d83b0319666c1b0ac520169c15be708343359447f2fd37960c1e96d32799ac9394e839b391f59dd347acfb79bcc4e34e76490880d163ac97ee69e3a0a6e68f +A = -175011349a0a1ceba11756bd528f2bd631c106e709aab223032d08d52d7d6724e8c5b055b6f97b48261f4860eae297badc1214cdae9b2500a7a47b4b777dd7b8f1006757754ff1143b637d2a3adc555f38eafbd5478cde0b04e5f46d3f0 +B = -2aa7f75d6801b04ea9f690aa0c5448906595fd28b53775059c01efe54b463f1d87c9fb4b39cb038e770f99bb995a2118b86ff8d004bd964e958c2af82becf362fb0b927c671cc3bd7185990419d26a827a2d81bbc0126e1029556 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3b4ad19b75e1301d19b57ba9b68e0666c28c7c5c99df1d5fbbe0685dc1d3489ff39c919222719c5d8b7ce2d7ff967730d776a02b36a86064ed66a02011bab82eb575390f85f0104715f6e4954a1bb28518450182a8ef58af35d00e2fe417f07ba25dd9c85e00c3451082becd22e3aa0c9bcedaa96e6423c7df6c375b4c799c65 +A = 58e1ce4a9b512eb0632b02cf1207936d6707b802140540fbcbbdd712e5ac1426b4f36e74a9a9ddc812e572855d4fe4fca8a0de6644226f5698fb46a5f2a479dfc8b588aa8e02ddb15acdc79ed3d17143e290f1317274f425b869df54a4807 +B = 14e341cbb5f5a7f3b4dd864172b82ceed2887fcf20aae7d0598b3d8afafd2f10c27bc7456c1488abb570be3df04f43d892dc6a8dbe7621f55bccb0ee3acb1ade989a510b4e0cbe29b6b93968f323f0016d87944c908824d249769f8b +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 7fe0bbbccad6032069b1a335b3f2dac16089051cd9321f903181fad23be6853e2d209958e8c48e008be94a62c6206b34b4e994ca08b8f24a2df0e6394ea65b3b7aadb3bc43d04dc9d35a77e673c4476dedefd4568b4ade5d16f9d89486f3d5ed0566b1eb428cb0b688f10fe3901037744f278385754fca481f937cb630f60308 +A = 1cc0e3ed58090db55063c9ba11401636f89262d6ec096d361f448496e05181c5f7f2604333f26d511c13534618e90637adc807d622097f7eabfc03266135cb626e1bad20997e72da71bf2b3f65a4973dc27d2a594b1fd96b7bf7ec14b9e4b983 +B = -87871b2058d33cb67d83b6a56ab27839c6a6c771bd94e55f200a1257f2c737e39c4a0403fa410ea64e8f442d300df1c19c2f03d07fb74d94f86d26814fca23d4cd2cd3718252cf0cd8a0e36726f6e68827a1dab6bbb1d23b884381c702 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 35d7ac5cbc7e6c262ffa41be168b02a3bde9e112c512d1f68421d705ea34461ce3e0dafde67f44d44cf31d91b38d4d5f2fbf8c6c6a44ec3ed0298dd58f3d45c04346c11e57229dc3d2cdfea02c802732d9a811d7be5e81094d72172cd04caaa3c9d55a951c09f454f42add6e89e2d8a98e124aac86379df377606e7af9bc6baa +A = -4ee01518f6581c560a186fa05c6f4bc26809c4822cc74a0bb74d5a6b0a368aa9bd0108f26113443422b8c589084ad49f919a9e7821d99127bb210670e732b7cdf610e464e300a39d3dfa7c82f90cf00ce329bc6763d7b1d4224a020095112fefa7 +B = 72dc8973f7af7122a05c90df190bbf1e39abca908c197590dc7ac41fd0712f48f838ca62a72a177a293ee6b2afa7a10c21e7993347c3df4f161a5641ff62ba123999bf1eabef29ec0d33ed0919818f4b7c35b5f41e654759fc9abdc0f80e7 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 5d83a9b34631dd6c63c05a0c012adf97b4d0f20f61907e1c2145330211e9a7e38128517b058e0a85e993c385068d1cec768deb814bea1323dbd333de091ad2cad72431f20c1e70ff7e1b119768ba44e14292c38b88dae7e55ac9e10ff98e9bcd5f0ac05af499196b4be0c6222d1a63227ee895fa6a8221a4a182a1323183cd7f +A = -17b3e0c9288be15fda58c8fd228216bc466731d631218a7ddf1d2c9cc858c0219cb0757d3b680bca1b1964eb15031b5b9d761a8bcbd160db89be339067a2ea35e1ac3cfed701912a17ef9ea03999d92e3592e893183ddc05cbb98a656983b54590c72 +B = -269f96a4634eb37cf8a6608408128587ba45958405a29827d0d03d34816fcb1a2297f1319485439d3e8594532545086efbe4d21d31d30e2daf09b74fa8cb27df54e8f9f993630cd9a292c977eee70887158bd3fa3cfef321ef900a0598ac8cea +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 7fc1c65eade94d9de7440eb8dfaecf1004905135efd4f98257c3295b1e76ccf1e2ab6808d158d360b7419c6210c50efe960610973d9ae855c72ec0e81d423e5863c80b542ad455700d2d0dee5fc403dc01eab460c24687401cf6a3179642e59f2a30268df95fa80dcdac230702352bbf6b60acb9ff5d45c5b09a3403b954d173 +A = 7906bd8d3bebb1303c1df1fea0b2503b0abe9c69b4f4f5bd01eec9e314788cb7d44b93428adbcef570477e8ecac2a64822e481bdf520fc381e1bb0b2cdae2fe94e484cef5236dd524e4dc364b72f4c06d57f29dd3c5079e532b1ab1e71dd6a65b3362df +B = 1479ef2807b9c23c094d0416f513894cc92e023b134f44a5333360dbbe98b8161ab899302f4fa11b470b97dca0c4e8ab7ae47e5fd0962834e6cc1763618193f4ee027f667368da580c623080de137b5869c3081128e6081b9d5e2dbafd791773242 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 730c04094b1ce944588e8291f7e6cf763c70b79cf362dc8a1bc63bb8790cd4cfe4eb51cf15a45a8464d69ddc3e1b9383cfbfd643f317108cd9ca6a6eaaea177c5c8b6747bbf40108cbc0437eb8f11bd2a0939da59b70c0c6129e2c249823897f2ee536b0427bc45035f121d2cbe7441c175899b97c490e6c3ca01539bcd05848 +A = 102cf23cc3b81785c73ac3613c816de47fd585c7d5f175185818dbb4bf0bd47d0dda9702bce97b29d66e48bfaae0fd07b47b40be2b48ed702ef21c54b10bb927f9d6b43604bec4f4b2796b44aa6b4e83f8bcd00f2fa3871dd901570e1a32888d8691454c40 +B = -cc5349a9c5280a933e87ca38ce458a711c71ffebb40bb1f7612b42b4684afc495e99c4a5f32eef1c9564c2b7612ea4cda7a0f5df6b3ec9026447dc565ca08563d46aec7ced9fc4cc5645960210d44cdc3944149051d569c9295dc50862f8f6d1f6cd1 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 1cfe1842a53d00e4619265e2fce7cb566ffbd912c9213925d01408a956af304eacb85e29fb6edb812a95e90769bf1c3d62b0cf6cd5bb8f8992391d2ad70f38a14fb9d1d1eb522aa7b7fd9f1b52790beebfc887193882377b7ce567d317d8432e1d9a908d6ccfe8d2de7de497d77b023b3959cc042ae30aefcc0229617fd2a146 +A = -5c3d24fdb193ed83f5f6a825c1716f98e3cde6b32e09659f253ca3fd2a39402b5bc3a6497ed7bc908838e93422559a13cf59156254bd3fe1e3b8600b2a777943cdb39b9d42c58043f1d587424425d3ef5f5538ea157112970ce3e09a87fbb5f7c96f1b5e65fa +B = 675d9d2a05288b438ddcb330acbd59e4639375f3f14ac2d0e9e8b72de6ffc1d217ce62f997577f7eaddbe4603541b132cd41f2f2740363d9c331ef22df92029d143fc8495ed0152b918aed7ff22f564c7cd94fd3fe4178c90365ace43def8fe30ab05c0e +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 83ed1948276d689bb7fde814e67fcea72c4e3509c48873c3e7349a8fa1c08ae11ea4d814d8deb1021eb8b8ceec342cba5002a2ca45d5f340ae1aa500af4c7db120d0402c6cc8a840404be7221bbc46ffa10236043e5ce4415d3ef1355bde26d2d26eb7127326d4b8d671bb96a08e38a2c1dcc281830ac77202903a5e4777ff02 +A = -1be86e7c87827922d2e8a06e3cd6b64ac9a280c525749bcdbfac4856916321a964c9346d17465378251e6eada42dadf38bc9d7d87367bec94ebdc21af6b1302e520db08a64ba6b39920683725ef02b011a3e4ba46ef0eefadb98582cb911d0cbeae9c231b5e432c +B = -352059faf97b433089a688c702b97adefd0c91d51a0395647f822c6762fee3287693e302fc5a5584a12c048dea1a320cb96fa70b5daff7c2ea21d249467d14c6bbee15a1e94c030e908342a939fbe8ae0de58cb6d6eae7758485e392ff6d5d64465b701692c +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 402525e19b6b68942253d1a51fd9b2ca36fc84cf938d80b3d52fd4302de142b9d93d1663e89340fff10c2b5efc8cd47fc3b5cc5ccd49a6ea3038ead6454bf190b7f88f52c56bcf00c6ad5b0f5dfb7615915ee8af137dd99cd3d21172ab772f36d291a6856a8e7912750139c09aa024b930a0a6b9eccc83c2c5c0ee2473ea32c +A = 65e5db532ecae639bd56dd63045bca39b33b4d70b2db82ca3d0ee8ca436e671828cde80217b48eae7487fe110830589ab1be889f1e1463f3b0757d529b2f0cdd2ac92c35e8ec141885bbefb6040a3b5e00e64a541913a38fe05824a929f8c5a2c46568c61989c3ca7 +B = 1d9c73eef8373cbb1e8393feb26d55c33a245c33d7031c234abffb2f06a1601f7f3a79ef1e8664c51ce5dba5f5aaf3b9a9e42470d381219b4616ae93c7f6e64792d23bae523b6a224c1f714ebc82a11f9be42618922b8d2eb7b55e4d45572e68a19fb0ba72228b +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 7a9cdb5dcdfb6e04351057d731fddb9e85f41eb432f01c0d980673d294d05ba9b0180133a89930e74cfce78ed54991b494a19e7f80f310b85904784cebc5639bbc631e80751807868e7fe16719e8ffcd1f2cbd1b9f303c3ed488b647670be3080668b5fa0e53b6342c33c87f0ca1efe1ddb1c877bfe2556aeb61805b06f41343 +A = 1e412c3d66aea2c503f3aa5dbad368a61d969a2951c0094f9da32d2794e47f3bf4c481ae23636baabdebdcf0753d431426b1865e62de8eae7238a9245d62820ad7f17b5380d701f5db776cd4e1ddbdfd542901731ffcea5bcdc247fa9c83f7e08a9389e5a76d38be21bd +B = -afd61df72361260484fade8b432713eb740df83a401d73492883a5139c918d5c911ff5dc00140637da1c6acfbab4b0bc8fc1f337243d90beeb1c2a083ad8069494c73a99372bd38712a5b5393c779ec1915e878600e0b48157bea44ca8e97c6099c4ab07fbda57d1 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 712580a1ffde78c8cf98ba71843c8130e835fee3afbb45e372d04c04cc388e403c9efac742611d7974bbae982c3aadfd1893f5da280afe0c1db1d81a9ed73b6ed9b7f05a20ce828316103259112d7754560d66733041e9470ae0d4dc95fd0484bfd56d66739f38ead7efa4051187ea41f7bea8fe5d958a29af41328246e2bc35 +A = -47c5755ca61ca8b7ea927f6fbe347f1362915548ab38c40f0418f4c9ba4ad520c3b2469d9ba3976669dec0b278461bae80eda53e9d11447512963e797f45460f74678acdd69fb9efe3897913b6568f8e03a6d90b4cb5bfb06af132bf118574b70e6bd2f6d6cb4d0089379d +B = 5bda68c0a64218d3609d75eb4832d5468298f19498507d7d515f4c410f04dee535947571a5e75f1af7f94a5b3b05fb742fde23e7cf3f8b3dbee0a569e5a36d7a3d31a26c4a48a299044fd72339d2cee1a68966c851e76b93ae34130b75f4abe4f2260207d2254d23f56 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 4a1a514aa4d1ada84fa841d0b668930c904783fac521377a7d622201867d773ad23dbb667e0d4181616358f3cb088cd157c8e72bcd03db64647b37aa1813f870cbb0318ae0a3667f8e6c19f6e0706217646ce633f0cc8bf4e8f0f4d7329a8647252ca6d376416d545e73cb9a3cba40f8f9465d85d57c2481b84b6d95dd42d50a +A = -1d68bddd8c3e6b78daa0acfc63a6f39e97f19527a43f6cdec47568d57b47f4e4b7ee88e4a28d683b569e406ecd2510351dba25f10b9f7c82d6da16d848bb970cedf7675e67937921bd334eec4bc8fde83d67aca57eec804ce22bb342167602fbff452d5f0f2a7f38b576e1e50 +B = -34d219765916a4c8ec843ebee9a7aa1162974d41cb4d6b60532513608452da9993749455d9701af6b7b6c7454d7f2fd5c344cc938baa5259301d4b56ae8d25b6f6510ae6bca114cae6791fa5a9551e8a405f5b1c0bbfc27138563b2d64f9a4d7a8f42a23bfacc3f1ec9393 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3fe24e66e381eca525b24cf767215837019f44ed4fac6ab118d02cdbd658066505ee5b0feb7af51859992ecb97d727121e38873f748a61d70201cc43228a7732156a80dbe399e05764be19e37dc1b93222bcdcbc45b1a4817460f7021dcf1d70e632bc6a306628790201222bb522f4cc80adcc907463a539b02f74004d42adff +A = 773454a43f495959dd55b8a064d70b1b1ffe45c084f5f9553582e24fb402b564de68e5379a8d9d02af101594e717a6c6db2e7173e557a64d2f28fd45c4e06041deda040705d99acacf8086830af19c7ab5e27f91738ffbd937dc27e5b7869bb6caa12c2d7930366ff75eadc570a +B = 13d884a2396268f1a8186748a15722156a172a56dd3d8c77b9cb7001b6ee06720653507eba9bb9918f2f699cb37f3b5ae514f5180108a704647f19b0fc075826153edda66dc1105c1008ea8ec6f8c10057f8e8e479e1a1274edfed9ef719b30827a30f26da78820c3696d01aa +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 715bab8708e53f76d2ef2afbb845bdaaf978b54ce25f84dbbf9074f16d30a18733a02a4ba5d7b092fa6c25d3b9b0d8243c743910f1b7b785d9cb02343fc6d59eb0817bcff05646030ce4fbb2b9ff76781cb1af66b46553d365d02c61e677ae97defe92d057d4378dadf8cba9824b0022c086e0d78b5442bf3d3263ba22c643f7 +A = 168186208c734383d472374fbedc2d5d430e85690a4881b740008623120a4f7f83b2cdf85dc28bfaae5870abcd7ff1bc782ef11c78a75c99d41f8aacb52fceeb5f10266dc65eb00b0868937340146d8850887686d54218badb97647a6d82c0c6650ca1f9078d73fc6222aab95c2967 +B = -9711e5b3965654bd9427f79c89a0b3f3cdec1c857f4451eec236c1f221bb6773e5dcc30e7381a18a813ac2b03ff4a4ba679aad41e0e5d7181d4627f682ca2dc8af9a8b4f878771446fb225a979ef9c7e641cac819c307c8dc50d9c1ebadf912ec7c844e416f95b546cf09391f9f +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2714b99dcde70d6c3be8b671d78abc155793f13105fd4b7c5d760a4c68ae89987311dabf2a9238d18299f983b8aca69a9ce398fdf2c9775d90b11b3dba17bcd8edf661efb6e9c50b4e37553cbecb54eb214fed1d0847287732810e550a4c86b51d4e5da1cb7722ce4317e69644620ad806d6d1c94e1e3fb4d87de6178a997453 +A = -75231ed37f1dfa4487c9fc79a6f7b36929fdca086e42ed41f79430b2dff521919236fe415ccce590e1d3b986e16dda866f3f0d29ac1adcf55d87fa5cb67dbf4693293188516e360bac513303769c42181483fbef7abcbc4fea1310c916396d29f37d9058a62aead94511aded7c4b8de8 +B = 5aadfe65df0e5b877fe45d42d7ca02882cb6c686d486374da5ece6f87771675153c84d74b6f40df1db567b7e1e3c60c41d21816f958f5576fd2ce2f84a8c3be4749dfc7e5561266b7c9698c7581292d0d813cb77955458d63bf94ce87472924c4ca79504d1ae9d5f025c7a2504156f +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6613b1c8ccac0cb8fe2f59e76fef4dd05acf1f1b2bfc20aa3f193622ce3e9d4c7824ad544477553bc68f05f0b546e7c1ee87301e111af7929d1f40525291b88e211db7175f4e5c0953141914fcb4fb951dbf77442e7cb28fde495704f1b5141de1e50fbd0e359d0d86ad709c8f564c84dac81c7602717c269219ab1cf12e809c +A = -1bc03897b02d1edb633e2c019e40c20c1d89a210b0733412aab675563fae8bd75dd7e65988cd8df4d9b343586e27f548becdde274f62dd421679554ed9eb127e527a69d69fa8b17aac0424dfa2a7692d1e63617ea45564b55f01a70325bca050862d583cdad96c4a2e123d0ed827348a745 +B = -3d5239dbe7bb3dcfd8027204eccf5e9444e68d322a0b0c535a203a1d0c054e7dc1e588bacb891388241462a5d2b43e6cce34ce46a23e6ef29670603d31001374dfa347dfcc794988e58945d0d2d17da6565cfea559203dec119fc357d396f65b296deb07686b0ad2d25a13fd4fad88d2c +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3a7fc5680aae875b9241200b9f4112a82cd624ffd9044138ae3cd65200631ee9d7b918fbffadcad7e598791a9f0bef3e23005d6bc0048ba92461283492df3bce74c66e417b082ee052fd8f808d71f3ab18f9ffc40f8fb51ebbb936d09c26a3514bf868141f7cf238c1abb3d88e5d50dfc188902254f07d63fb8cb611ef8e4149 +A = 4a30f32d467b29dc83b40bca2fc4ccee5f08a64069cb87f20e63387b2219b12aa312400c4ca59608f50a71d2535cde40a6d248290793fe01693ca40b93a5cded2dcfbc9aeb36e187c9d650782d12bea917daadbc6525f266e074037803e4b2f300778ca8dcb304658cdb502c93c94a16c6261 +B = 1ca5e5218dade077fecb81d579e1c9290431b34df5ec84aefaaf233d68f17dcf60ee010db26320685af13a821b6daa9d73d8f3a30826c3ae7b2bc5e219cadcff826283cd7dddd04cea7a5e0585d6e7c9f23b27f14ff815fe53bcd75fe700b1b91671bddaba737fb43bfecd2a77e5b752a206 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 768d312175ce7d2601f30bb38339f046e4c2ba5c19ae5f7ca5a562cc2462c579fce9985e9e8afe2578db542c8d9e7693e0c74ba161334b249ce720d568e9c18f09c87cd701e6f2080b752362f2fe6252a1d0caaaf1fa18199776e4c6078d89d520b9c63db159d5fba7e0838811e68794b1413c248f3f7173ef29eff28f15b656 +A = 149353e91bdb70cdca8f06648388508511a64d05221305cad7187ea40d9ccef91fe17ceb1e79667bf66e8e6b7a57faa90a83bad119c02984a8f860bc1f23ffd33d4ad84896610301cd2e8e80a5ca7e8d3ee63e7dfa459793c9dbaef3569eb4f8a021c6a3d032a9c94d3f6b8278274d0088a98228 +B = -a7cbbb6a434e4b022d312ecd4a45fc7fc4d3aaca038cca0fc56e529fe7119ccdddc8e76d51a2fb862ad3d27a16ec8a51e5f66b9c7fdfbddcd05a0ddea14172339cee340c8c651eb653c6aab6551c99ae94f26116e15dc62f2c2e63305bbf84590fba1327ee721150d46464d7e22d45d53ffd44 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 763912f4b16549e6ccd60eaf7a0a1f64d9c3bc83e4a9b87e209a3959ba3cf609cf47183bc543f08e346b6e12b8bdd5d1c07c603f74b286ad432d58d7001299ec7a4dcdb56ca875dfc7ee5c75bcfe2aaba14959bf3facaebf8df92bc12937cfd4a4865b3dd74b243ff62ba256d110b01b4089730cf48efdc66fe272f9241014e +A = -4df3899b40d51c83dacb442fb143835bcdb550136921df78800f0515a6cee77fe3236dadd2a0800b79ebdaaf8cf4aba5ebb60cdff3e4b4531ecd0903c1674a4559339123e9f09158080fc53c4c6ae72c961c8da2f357b7c05368157b4956e592c41b25642457651abfecb4fed5d9fc1fc3825b772d +B = 450eff382e73f2f38bc3a4abecd5f8de478f80a6b99fb6252173c90d7099629afe859442bb1f796855ee9a2940f21d1f9dc44f462edd74b479e1f2926ff6faefeb55adbc6152b5c97967b1dc8c44dfb85b5e02e870d2920b75422c8a427e99e35e2a4be92cb0ddc04cb7f4044f716be97b36f045a +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 56ef57d56c6d1b94cf0fcdedd3611a8ee444c2e25522b9ad175587619598da341916b183be03b1e73be300f9969120d8f3a23750cd8c4ffdb87124a2139e8ff2c15d8dc944bc3c3a066aa16dbe6dba4a74925e16acdb2b2e83cd7fd5cedade6a7f7409a509c00dadc182b2860609cc9a375cb8bbdcc350bcb2c0df9b3bff882e +A = -143caf995b7783b1316b5551978727f06512fe114b419c735b3381ec351275fb7fbd6ca88b848c3e8c9faedebd6d084cb8a231636f68f6803d14bafd90534609d4a4ac0fb953417be7fee4e4cfefa452c5ee5d1e1b97ee75f83cca8691a0efeaa8bcc1f1e0f18c0c5d6c7684c9da6c9495d31a32f40a5 +B = -3025fa05c55826c40089b12741b7d406f748cabf692bb0227519a124653160142633700e3c0676000943556f97551171d231c1a35f7b7d8f96b0366eb74942466ceb4660f09aecb2fb2ac050ef699eb05bd8834a2ba959ac71550b5c026b9093c8cbbb7c5fb9390a7818db682b7c11e58996c9d0add5 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 42f363c34c20c443c1ea7a1c54f98c6977b6671164a80308000533b2404a7f280adb1f3b98101cba25249131288f7ac68b0ae2572c7777e7381c1f4d05fd82188c4b1ed5636652e0bfca4d096bbf4189a9358b79f6b6333b99e5c4b7a940c2f7d1413bf9f47a2ef66b620b5e220b2c3dd7267452eb1b9d8d9cfb17bbfcdb6abb +A = 499d05de867bda3118a8cb82b80ac91fc505e0fbc6c7dac5fb61713cb6e715f56a31ae8af4b400461d7ad1687a2631faecd90d7829f67d1b9e36ed7d55704b3f2aea65eac061172d698384daea710ed92cf1140cd4da427174bebd173c2ff1675b2407a84649b0a318602f33105006fe4d5ed8d0e015b99 +B = 17a426a12a0175bb46bf7a7e727eb5238af383cee6f4d5e2bd82b0d29b9fed35f3d8ec95cfdfcac49bee47b25d3b5f375a3340fa83f8dd9330a593a974d208debb7e567e59dbb7251b54e42dab2cd50fc63aab050a41bd88282373f8195c94c35f61bb48aa921f574cb4ff0984ccedc070efea8c46e5cf8 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2f03374e9596cb56cbbd89794090ca7a4b437f4c05fa38a09db60e5ca900b208fb85b52f71c29fd35e62c9f9529d7ffe46fcc54607ccb07f6f8e13fdd4ff1185033ba4fcefb1ed4bfc42c3ea9f05276767d8dc9b7b4aea4c8bc0ce84951d1f590cec0751f73667db19060e2bff64da30fc048a1f5700fe3f489920675cc3540a +A = 1073531f678877ba854fd1e7f857659614c526847ffbe8ed131dc9f2ccf69e1f1e917bb44a7b905f7ff758f61c06dd59ee09567d9f0df2550fcb98b776ed1381ce052988aa08fc5153e31c621c6a51ca61b386e3a9163a5cd69608b3e200476a8ada35d906c41d044bafe71ef5c6f732935f15b53bf36f7ef8 +B = -de3563925474e5408e245184b57f328e265b6cb62eedcaba809d8f257eccc0a457eeb82c451f93af93ce9f36dd1aab386e7c02b356f31c2d170169dbe15e70cf5bb9073b35fe0e7c7fd7faa91c5b2b0740734f12eb741a9d9ac6dcf7cff59f6e16324ea39e1e07dc5b9daea27ac674dfe5d0a5790abaebde9 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 1aa22f9013bc1cdebbdfecedf710c1bcaa41c696a3d7dfc1c8c601fcfcc1c85c8cc24be7df2cf3c7311b3b17a4ef2dbce545dc467d2a92d371e02a196a9977cb9042b236acf99d8c0d34a1c4dd8792d3497cffbc87c397ccee5d01fc2c89ef051324a7061e423720d0a3821a36739797393bdf7a45b5fc600824a17043312bc +A = -4fb2e3fde2a0c653104c077cc6459c9234f86cc2d7b317329b68289826d3e2b975f1a69bed1a53418a0dd86e1b2723f4c4c5a29d003161e667c2315ec24a36f8bb5f2eb0a94f261e791bb829db685cd0ec9e1e301dc140ea57cac1da228124ae029e2b8ab1fa3ab99c55a9ca94dc7b767162c0a24af851fbb984 +B = 63702537a07971e399aa9a1a0795db052d6c8185c79107216babe11d6d8d472b61e604cecf9eaa6d44a2fcdd1ef0b6b52226ea0c6902d929b09e16576e6d1a6921765b2134c5d23c69ed61f36ea9a5552e5819350366240693558fac7a9d09ecd3702076c8c758a4bf6843fa843dfd688bef3f73515db31bfc26 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6acb23ea695d4b60cce53079390da3cb3a4bc3a6486c238c421f3bf6c93c027a0475f656c3e5435f0211e90458ae81772aa956ef284093020f7b58ccd9373f3fdd39fdf4adb8dd64590f4a7fc05238ba20017bdad07f5f9a6f076b71554a7741bdd8c98ec68f8fee88396cb1f47c64d6da4c228caa3dfc7a9a1c032a9ba4fedc +A = -1b2496ef929bc673042996ae80f27c6bbd33fa7c20580240ef8fba985d1a6117d6e746989924e34f281e7d2509175d0773dd999bde16662e88fcef52978d19cc45fbae3997fa580a66171d398f4f0e7605d9f4aa4f728902cb886e6b6dc9f0161e7cf1ebac05a09c5a1bd69a92273280758173fd2c14550ec221275 +B = -28399206ae2820d26a5aa0bddc4903776611d08fc4cb34a22a8bdc2a19e9f8cdab94217f346a8070a4145f989e1dfb49cfd100267635af0e062872cc879c534ff138fca603b5d45a6860ea85b6de37cfca000c81fcda3d14ffe81da919b2a25214209b085bab9cb511889665fc845acbcd038711533da171d8308aa +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = c012c4d17ea4c95a360218adfc3363f6d89f5aa524aec70049ef94c2c05e59a66ce01e25588e164bf2412f9517b7740de53d037e71ec3a1d426f05b18b128c41a878da75421e8c8ef3ebd5effd40735c00818eeb1ec63182b44e817403c9f1f6c1a0155334be63a3a15109be6d45ac0d1b1ef5cc99e9b284b00c487d91e5472 +A = 796fba6276fb7129eef2d1572b305f63d7b8c49371cfb3b2c67b141071e66ccdb5e321fa2c1bcf624c77317e2aa135e1137dfa46a34c3ffefa2fa3e316be81f45614d422bf86fe4518c2fdb7e416bec199de033cb5fef7f193a80c0f0e6ee924a12c8f705f5ed3793ab770914924b45cf2578bdd09c701169f0a881e6 +B = 12cf934763127284e642ddc232b1c889cd86617307b6ad72a9fe0d48befd7c5c5370a0062dfbde2add256dc0af850813b22320ceeaeed347eb9319bf22320b2fcadeb51c4bb26a160f7459fc172c27a91d367d5a232d00cf7bb778fba83afb744177bf1ddf45446baa035fcd0065f9b493d92eda37e9138f4fecf3ec55 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3de123bbd50c35805b943e76e97b7e664eb9feb99860750bf97e275029e836217375cc1910c13269ffbd0bd72bb82ca445ccc4b693742a96d19d3dc23f78e5ccbba46d9ff5975f239551c36403ad5fe86997536456c4a5ce54807c24e3b5317b1c7b2a1661aad85b63859d427f0703b460cf72b9acd3f87e2e69d7f8f15e972d +A = 1d0433d84f1de082d2058475e0168ceb369013a67aa9417f066c29c28272a0b3f8be5ac7190ab78591ae72a1dc8ce628c683281a9ad563e134387b9258b9c96d2df288fc118a8cff068ee49d635343772c2fcc252facdfc93112358414e1734d6948b909b53e46263e9a0cbffa141ef77bc98e7fae8ae2bd85bd875aa7c1 +B = -a31a574d105305e47f4fc00ccea0cdf854556886b524901c22e6f3b59a42915932ab209a8d5da29ab70d1472dd5378d9c79a7447d17665f9d1f1edc1e545e417cb65415cb8a368075c16264f42555d26e83adc704b5c126c6129318a8f394af8bdbb32c8114470d11b2acfe806acdc7b96e1e348a32ff96a988de76d4623 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 770f0c3104c0f3395fabeb75ddfa2c21a111d23438463941239f7c63e4b6e6832b84508ebf3cde1d90cff0a2801beee05cd5118f9a726a987eb58def6780be899b473ea71c697557ff63a4c6db894e9438595acdd98abfb529d75bdf3c1d619d6165a9edb6aaab8ada50b61a3a84de654706a9aedb7321b0523558e8f18116fd +A = -5fafbd498d610e9f29c38a5c6c262b71672fe9e9c84f0f071b549390353e4fd0101a059b7c547007e27df97761767302458f1936395142ce5776b0959fc5ea039429d64ac5d50c2ae0ee45d60c0c50b7ceb4ff9853d57c6e883f588017ffcaddf5a1aa3e23ab068877a114d9a2cf742f01f5f5d611424c8ec0d082f5c165b1 +B = 552155ef110c126afcb87dd20251220c7a43bd0215ecd22249a21c93583e120ba6f046c6fe03086ef3c97311c4d520110a450470a473d8633e3560d2cb44c25559af07516aff50d6d176e8782c06cd9aadd3354cc695c4ea8dbf85e01dad479c8e8438154351fd5fcc6fc7e9d2162ce2f0179247f756f0b9b34b54be74821c5 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2e9ed66317734668c4c354d720a011fc65bb67439b2ac9203dca65a8f567682be40cbad4f55a83e836f1fc135596b624e4327acb085a61b6398237fef5a6e6560b488d4a673b5ae7d734b896d9647d71087621cc81e94d58e01fc2cc2dc775f9ab1b6031840a672fb715b77bd636e3d87b4949ec7bd60721bec8f9907b7c072f +A = -1a6b046d691830d33eecf2c53953676ed3f6fdd20c2252f6e915052ec28ad1fbf7a5f264acf87ef8ecd515ed921ce6b85017f3d8a8f1d14f269f31e3307c6f935ad468cf012a912b0650a15106fb949cbae7b36c9cd496538bb0646a7a28989dfadc719424519bfa43cd8833d3a748c758f813881d83c98f7cb2a63c2a4d06b8e +B = -34f87db0f839af6e4c4bf146789db36b3d0bcebb9bad81db690ccc3a35070d8830c9745b2fe730a1f3a252612e7026bf9889169b57b8984a5479cc4cdd6844ee3e150a2e7bf7680eebbef30e0591c895cc8b2ca488d489554f2339e2f55598717ddd8ce444a060cc95cad9eb478491ee8d3b8358c3762a970224abdc1068af0bde +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6455ff7c12bf3bc37120fe3f1302a9916a6ffdae6ee6a37fc23ca2f3a7ad910dc0e1027d4dc304a8eb4eccbcf3c87cf52a13dde472c07e2df2420c1d36bdd5e88c3d76e774ccd2ecaf6a0ef55b8c60231b1348a738f812a4fd9d0c158fd5a9fb19cc7cf9f000860d4cb6509271c8e43ae4193843324db02a029beb58ec2955ad +A = 54ec203e2ababdb0348135c0679eca2a8e778ed46e53f195331a48d3828e5e40da804ecf95eed819ecefaeb9c5377cc1afb1fb220175990d347981353e7d90637adf8cbb16812af8a3783dd312d967a490f8efe3f23746929cf2a5a8df58e0b878367f6c5e4d3c086f947fc2bf70bfc3a0008a8bb1d7d83f002930640b6ed94c334 +B = 1311b88a05224e15f1465c8da26784dbaeae84f818e029301ea39a982f714c64312f9f02d094c401abb6a89e8537d64c178637364bd261f4a27beeaaa901cc7b3d4e36ebcd9453cda33d47a53c6dd1d121dfb83a222cfd16158eac23482c8abbfaca59e765f6c1fe871d884d281793eb19f6409dd6bbe4083bf762ef24c24f0127613 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 64104f6c06e563ec66de4442d35d88117f2535edf9e012897f44daab5a1b8a8696f84db7a68d64ae24a394debb993bf6734c9df542c7e473b2e497396ce39a064789d5d7b339b65766b002a18096e7fb9f312ea5997c2a85463fbd6fc18f25769ac2a2123ccb0e72f14b0608c4c22add72bda138b83f986e78d5c9da31b15b9d +A = 145f580c2ebc6c0354ebdfdbb1d3d7fa17f0b55493b0b9a11b71001c840a967dc77f0206c3dde161b5a773a6b5fd9471fa08b205cb6f728e3afba440b55268d6a9542e234ec313d53583c580a391d8da5943f4a900b279ec9d8933f2cfbb260b74ab714a8b9a1af3190d914b6e42212df84f933a237728a5fd5473ce2e272eb82bc83e +B = -c67f9b9295dd5844307b8fe3cb9c1875257258e4be6229ab097e148c0175ecd0de4d84fe03c8da6e27153c709c2526092b1abc73b5fb40f1d4da9e0f3d8d2fd5f8a4e6f3c30befd80e189b73fbd77e8547b34010d2aa57072db0f00537cf3ced95eb517b23e0c854b4becce128a575a31037c3a9e106a476d8b0277d26dcee435cebedc +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 11913c40d577f70a5346ff1cfdca492ff52b640eaf257510d311872c8df7ba9756973da5b9206c6e5254bcbbb4bcfdad5fc4594e41ee44e77f168e2d20a4b228480a9908b102dafddd039ba7f7619eed7057e8af3a72ee491a61dd049bd947e5b09a94ef94d5f336945f47104fddb8493ef22fb648ff5376b68e96c0555d74ca +A = -5537630b7cfb8daf76d14e617f7b69f7b75b472801a9a818179d83ef2984d0abc8ea4214ed3d3d2bd785060e9c2819e861d0df760fc1daca8340e8a2c997c9ad201d6d2f12a82ae3883cf9f5c51ff1c25277c28175859a7b8e5b6cdec7cb3875071cbe415bb698b85cb19f617162587516f93c728ba8b2cfc19f238e2cfda115b8ec0431 +B = 597296cb27080f33a24241c1e98fdec32f7a4013a7340d367e4cf2a521cd462a2803109c27fcec353a30dd20053a1f744394fed75829e8396f8de434399bafd6cdb6e0ee81343f0cb99ef3087a7c69bd43bd722745a46cdff0c2c837fd87543c3c63df3896ac101a145b478dc224644996fc72460a89beb5741b91a42f2fbaf0d62c099b32 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 42f420adf5c6b32ce53fe23af4e392517e37013b8c3a7d035a93f6ff45142b0b0bd5525cde85f9b7bd9ce219bd3514617e89ef4d9279cb9a3e89e44f1994d72febd23ffbdb0a4f19cb76448199b31c5cc6d7ec1e46fdb67be1211c0ccd93c123d56ac0d9cd2ad11f0c58c713165003495b75b60665047ef80f6a393474cb727f +A = -1c6ac9565d1950ae6c55025f76e0a040eed0462218e97aea87208ba879acedf413ffd5e63a92dd8658cf5f49d633ce7b126091a55701168ee4932db004dfe8c35c939887fae3a892b0b04d8eb74191bf8fdcf5566b4d3796a5d2596b1e750f64201057ae60aa705edd58aba4b48f6a2e511bf5007a6c44a27e3efd5bf2708f7046c1fff7864 +B = -244f2a90a57e5d066fe22f4d52f91b44882b8ef76d1dafc3387abcb224eda4a2100239e729bbc745237f8129d457e98eafb2ede2f3afb81e63520493da2a5730f1170b31fcac21259e90c894f8bc488c5e5dab2c2635bc7b1ff56c3685607f6fead73a09f83a7a168c4245729ce5b06e482d7d3d72eff33d14cfe2f32f72175484ffa292a9af6 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2239459025b257fd0b6659f54b8874f93f07f4d6240f8ad761c9da288cf1537d8bd001eced284bddf78edd611c7f28f1393c6fb879aab6e7df8eefd347d63628b1ae086148f488b01272f67ca19db71a2b284eb17e17aaf1e3e8f23ea253595de474d5cf47c16aecfae360eab7855868b8af361491f6ad96f893f9d3eb66d07d +A = 558613de283911aea1ee21d6b926f531f778c5226e978ce329860682b5375fe5e5328ae27b00f504f2a2d24470d16c1edcb8e76b4d1a740e55538e79ac7da4b45c5299993513ec3bba7e7395dc829a00d4e228618dd348fbf838eaf0bd50f6c70253fb1c1c734a07d0813915be25d3163df13511f3675022cb85af7646c14ba5d13f615ded8e5 +B = 1f3c3c468146c29408d9207e15b25186d3b06b3fbf9556eff7ed7ef7788032d87ae1a4d2a0983902d4c70936c615d8c9ee26c89af8b58d60231ede54e859763237d5ac59af686300a3e92f456484ce77700557ddc0f93bb40e5d2e5117f2356ac7ffca26dcafb3ce7a5573e07ee97515b6b082fe75fcc9dccd76b4fd416e69a247fab2b30965d9be +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 7650985e7c6e5461268867dfa9782cd8154bd6a4bb5857d6555e9d9746ee79b37e44638940bf8d5e974911327f0e53bbcfda0739056bae2248015c35839f35e7e359e93d3a339e7af38c0cb43eac5b41e1406e34cdd4afd458a5d126f70b5d683415b490e0ad61269ffe7ea8972eda6addd447d97e60891e5099ee920e18f233 +A = 184845d3762ad1a9c925c51fabc7b9e15570a84a06ecef994910845d56869264273d75fbb84a31c97c27eb9779e8b39f6829638a78b266326b60546507f65128caaaf36d4e7f85939b75cfb3145e2b1bd8372531cda579f59efa0da9c95a8efc72faf326d35c660b4444627d328bedf50a919029dd164de051a4c0c924103e365cd640b9637d8244 +B = -977390f52af784b52c1d54e82131b072a1c308406e9b82587102e67c6f7145f0020952231a5f0ce9d130677bb5a7a37d5a06dc570a13a29673c8a9068f06242ac438806c37ec46136e7c1c1487ca2d330fc1f3c1f42ea51ba2805b74c44a61fb2fac109710dc3dae78a07057a753898d4e849b910f035bfd807178f0108812778345b256c7b59f8883 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 35d48c3e43070a10dac0e256afb83b219aacc0036f554bd998b9092ce3bf87bb5d3b00947f2c86fd4e7ab830502d15fb2d4e47ead087f5c779a9ba56e272ea86116e2c81345d379dda6b581e9c8f4df8ea56c78f04d4f7412d245e00ac645847af6ae97d5d2ab27e48cc878d8b510c2dc753f6ceb1b9e7bdd923e0e065a6c11e +A = -76e575cc79d7f0c313a489b255e85d114f3933383cdfe75cfef649f639921eefb9b3b3184351fd0ad252c6e477e153ee586a0ff6da1e1b2bfd7e953e6dd778c849843fa5cc355b31f5529ca45aec81ba67a1e364d5a74a4656d266f7decdd47b2fc2d81d6c298afa2d1c39b5e8eed519a9997a14513537cdcddde0b5b41314476264d59b7d3f0e9a65 +B = 6b7faa437b4e8db8fba56c62eddb8a81e9090d1b6655a2185d656b2db0e85225992297381d653e707aa15f3017880b0f07abf3dc455cb09c4e551b3df3516c6db4ead79b88339fc33dda96bba76ff7c388363c36b67fd5dd0ee63f92f67549dd77e37e9902ae51cb58057579f03286fc48e3b7fba763fc5844c222e6a1eed9e1634d0bd034cff222bf147 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 445039f359b55eec647296fbff4f22beac09cad32cae79c13d591e314fafc2b77839816aa4f641250938865b0a2c30a10e23da71a6dff5985ebf3df4429fe64c327557b12d987ad9e9971f7c7b1e4ad01c94e1e5322dbcbc4707a959a401624619029558fd6f5b14564469b13146f9a2555916491e4d77caa70f51716b299135 +A = -18ddf976fec2090f7d1f4d41b8f875e56c813c04338f595d6e591b3eabf9e105be792f45354ee9beff997e6c0e8ec3fdc714c07b3466ad1a949b9d30da0115f5484c3b9e00c7cf0c117db57c3c6cd7434371c6d9ac7a5da1a0e2d705bacfc22f62785222d59bb5bcd3e3bf2df8e845953c6ddf1b546cb75b1698dc8e20bc611294ff288056723f1e46ec9 +B = -2cbaff39103570df7d85a5673b50fb8818434bbc19ab4e33bcc8289a4047d85de1b7029a5cda3976ab12e1d891b7efe3d5576bcb3713c597771f93532853290068761bea04200fcaf9b05d8553b960ef5e28064de89d9e5097d12b26af0b64beb40b33ff82a55af7c5838b44282917fd4342e2065942c724f3cca515d9142fb8e46652242e8f0ee5ae07b6cb +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6727c0d0ecb4a375d0fd1bc52146da1242099d445ed9e87b1fad4daf8369fbeeec49027d88bd98efb425c1e3f73e412fb327680068ae57d4a53992f3759af0ac1b96a92f56c2cf552e6682d1fa90c3910bbc5c0b1754862ee13c5ebd62d5b98bfe8dbbf9bf53bf9ed0b967f3c9da24d4334b9f3f75314b429b05b8e27142623c +A = 5cb6c49efc6767cf956885690ef740337aa71b90c1d4b9b0a9e4734de0c0c50f2358fd45aeedaca6e1dd0fb510bf097bf46513ee09f3343bbd1c11f507eb61d51ada40c5d6b730561756480063f60caf05141bec9a769c241d367cb92fa8e229ba2e471fc73f48812a25bfc7553c395ca77b80443ccaa82fbb7198f8c35c3b5a2fff977d8b2a29cf9358ee1 +B = 16ff229a0e67a410555dbd4b687f1470ec854ef67db73a902f2d19953c55071c4a26dc320baa8571586f1fd54fa490b0d87dc83e5bf20b78956084275518b307ce69aa4ca1079e3aa753d97fa1cff62e0b5f3b99d96a24e411fc3a3e375ea21b7b35a578a72df68d28286fd9a324c06930905f696424780083715f77961532bad061f3901ed276a9eb6e81ad4b4 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6e9947beae4d934253e481d27e854a59c4047eeee4fdc7df7e174a8f045776109c148ba3721685195b8fb59263def88891c5953b5a0ae85fcdbf02abc76f4d3c0f5d9496327d063ce8b3ba875b4f119dcd8beefb3ac884c25955af61c35a69d0670c3c349564e5b84f7df4252d6d3b29d9a75f09e9ef79f0fa9f797bf75b8ccb +A = 188785951a3befcab56128cb6fb9576bee2412e6cdd7dd1bf5643babae83c8011af99aada405e119c3be33653862440005be994bf37d3802cb6c73cc312824c56841004c8e871ffb560e93a1d222c93d63684e90a91394b9c8ba8cac27b414bf818ee0de7217bc2faf099783800485ce2e93612ce39fc7e2f1db708bf9bb032d92b66159073fecdb2e0257058f +B = -8dddf094f30284c213577ceb7f1b2efb1e4213a548e6aa840f801cd6382fb6d4995908b7827078dc3f46fccdb9e071bb8531ea8971de0ddbb714d678bb71ba9d961e58cdd5f41b8472146ff9b814a5d1d6368bd94812f8d38f235f39aeb2421a57499fe7102c1ab167df7d33b32a6dc7c8eb8f4babdd6b6c929d1ebd9bf4774aa40cefbf136feda7b6e10ba4dbef1 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3f4a8d90017dbe8e77205e65fa7a0875a1ace6f3f215c2974e47dbac779804143da3dbce92db391c2614c078997c7d1a15439ffb51a5787f5bbaf98a4dcef576a6317b9b92dd8141a8fadc05d3be7c150630668e620a4e07b4b00519f34e422610a160de112f1ab8adf09a9169ba95b60242c89196ac6e155021dd84b3054511 +A = -65ff4322f8e46e03aa6c1fd10a207a5e51db6991bdca232c0dbc9d73ba77fc485d881868be7b14c25b05bb59b7f5bb6c4b2a7d53f35d2d7af282a0423285c5de656429ab7d3af7d92837e41ca701f527845e98c2bfcb51647512e6abc6675cec2a7d34ce55ea4dcfe9e7a8397d45a7a3e73bdff06e303a8f04ab6285eeb1bb78b1455931cae203078eaae826a6e5 +B = 4d936b603eba3aeec3d3f1f9acff02a0ecc28a8ec64b6bfd9b153b1bbacf4f1e186d3deda8c1c81e759237921cec53251250e3e838f5063c4a1eb6cc93637f35aca10b965533d18b713617a312e74c446d63eccee93cc97e3723ab27357ae9b3cbfcb3e2bfc589a1bd582480e776198df047c3ad85f611ca6fa480c70aeb98af02f57d56dc9659b2a6bee222dc3e0566 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 8a7f3cde3230af1f1fc25e0c0e9ebeb69161d3864fa5a03e5d7f8c82d9940ded285df35c008f61cc151b4578e2677b2f2cff3236935de5bb1d113597eee448496fe29bb18343687f6e9f1c783863e949a0954de2993d47a03607423b458bfd18c844ab57e9e2a43930df159ce8564edb5a2a37a06425626502e3ff9363b73c79 +A = -100f2984dc1451fd7b71e5d290e4b7de2d26175a47b9bed524fae02bd5abf96faba06e955107329559bff3805689633a4a57275732bc42183acdc792cbf7b6b24dbdc8921b73c0308d0c0ce5d8aad75f7eb16352e67116e859b323deccfe5d9ffdd1f0265297bc9eede073146a06acc3c330458b07b8fd0bb652c7325cafdcfa165f69cd0de8b145d49ddd576fdde15 +B = -21ac4953e54347a56800d75f6feb6ad660b0442174cf3c5dcbcf6528e2b5da95a614d3a8399da14507df4b8eacaddcddd627b10ec2dc5fb8c43d96a38e6dff37189ba275afb9484df800587f4953e327af71dbd58780bd5885b4cdab15ea0f2864f961bbfa9bba6b2d9448443af87c0cf178990254c1ae6e19003b1621f3240a6e5d0a3be2deb5dd253f5e1f88dbb60b522 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 76f8b44df8d8547f8b3d8537393d2805c699eb37d19bd115bd5539adb6b6a00d004def3b7793d5c71e0ccd2b7e9fb87103c1a5f56a8f18ede1bfe1607a346297166596aa78dc584c7c32832e11b72fb4f2d40ae1591f341919bc0157080ee8febb7fee5461a918d2178fa407c37a8243e24206ce2c19c3addcc2b7c3c1912b6e +A = 56f4d397530f5c90203df1ec799f82a0096888fd370d543e33b5a2c8042108bb75a86265204c40fa5a9a44965ad2fb41896b134ea56c79699a230f38c0e3fa4e5d346cda70e0253b9993c9da5642f4e645a0d96cb732f8f04c99a83d1f1360a385c6e1a972b89915489245ce58830788ce23b9e62d6b48a7ff9a486614d6979033f7914a0735d201c6f29e512374088db +B = 10fe818f6af7a95cfefb0ea0726f9a3e0e7c30dc9785b1fdf6e2b810515448386c7efc656479794d389e109ef3efe37fa6124c5a7db3164268da0d98538606c57bd2f7df9482860e81f272a27c727d7d81a66fc1a9bc8c385cf02b7ca6bc7ec2d8d6ba1dc992caa216d02c9bf0fba8ee754af77567c6e275ac1b6b1b36b065760761300d156e40da8445712b8fb206c0df346a +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = f580f9d2438b22700c3ebb23d1dc296f3d33deae2d32dea51c7ed3a0ce7b06af11046bc1cc279bb744bc31e7f822c17ffcc5dcbbdabe213bf97bb85c7e19ee71a513bf59b25b3b5787e42e9f3ef6aa1acb8705d69924a107b4f88e0cf9276c2c7c47fa4bf56c4900b557aa5587418f0ddd899630ad3ff678b5b907c07247b2b +A = 1017a4fdce8bf41ce804b7c9c836d85ff6ee899807e1736bf0357b015b701b9675297e5ebf588ac6c295feed3c6a367987e192be0d89523ac7d64b0b9576f311b5b2705c5398276a52f06085027480c2ca72884ad7be34967bcc6c8cb4ec4fb761e88c16866a2e284b40180eb14536810eeeb180ab701ec47ece62af65a0753f95ca657e7d04ebf3c3a7db02993da9089840 +B = -aeb03379fcd4e87cfd18957a72fce42e016951a72b673a9e81f666b3cb20d2bba81400ecc2b38601bc3270eac46a633a1a6b55c50f00e9d7fc8a20176b93e971cfaa4f41573b17b8ccc498f8a3230825afd0d7f102daee347a9d59cc0914ac8689c1d8b39ccef1f3def44054307a7cb7706535f0cf4007231ba21696424c3d5b42c8e85c278f7c2e8b7d1787effa601ad357eeff +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = be05efeee19cc91e30a9277a6551aaea63aa3861b63f6061efbb0b92296e09f4709529eb849d9f40406fc59c526a4697144cef9661b556040458940ffd6a87ed56cb073d2ee0e6d1f05936fddd1b9a8974a3088577847ddde6bbdfb3d69158d5b3899c13ec78fb5cb6aa7204efe308bbe0b52f18381fe838536707a8a27ba0d +A = -669660e75eae9930dcbdb99c477c980869417ec9c0e8c4053f0bd8ae62d496daf7539f37af96fd1cfcf3149bc02b8182a46b413e3397b49d4b4d204491440eea65505cf5d33a8e797af08f3da41f5a0804214846bd95d730260c6545d51126278181719ddd396c55f119e84da71f0683eb6db8393b098b3a0c5999862644e073b4918b5c8aff17efe860744d85bc94b582d45c +B = 6045f903a750b69b709cfd6a1c8ec9fc0d7da9c53a9d26fdb0ce9a17c6a0ed5ba633d6fc01f004f4a48cf247d61f7df609008ca5bdc8eafe06dcfa06bb67efa6a584b5a2f02768718a908978edd475a2d2926af2a6e523549a5cbecedc78323c5c295bc0b8d3e14053078492e82e339ea2c6301412a5dd7efc20da0aad0577a37d853eed820776e672bc6d23dc821b5855eabcceb18 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 705bf20b7d92e68a69019cfd721b27373c7ff22f911066907f556321371fba70dbcb9774d3a26ca43e44ab20c586a3c1546fc3152ce011be66e04a59c6631bc8bde18efb7bf1743b9ed75a7a6c5bf5a4117368b81b112a3cd4e1c44a621f534a11c426451ea5fde880939ee5bb28d9843730e284520a976cd9f60c94751050ec +A = -17c1dbc1ad1d2d33dfe1af7b4cdc7b69fefec5a92656957e111aac292e44719c7c752ace33dc74a6568be38b576a5ba174bcba77a034af5fe101699c99ca39f8a3b0a20679e6d0180868a232fd8fc775089e185e5eb81585403f32619a2f4d857bb091a824a89de2e84529e5b0702b45771a5816c5a823d81ddc89f8a70cc3d3a0c6bd6d85e9d72b69d2713b61c46161f7f4700bf +B = -2252b54c602456c5deb86a0f249f3982c3836b70a946f636b22fe00c6e3b91b94e19200a33087fe734ce9a3f92a6099ad03a95ca523b7edb9e1ed3464d38fb96c470464e1c54790cd48769677efc5e1d22f5be4c15288bc5ea1dc184a05fddd5e576b3b4962f37437b4f9709dcec374377db44c8ba1d8611c0c3ec35f9bba213eac59a047e78195ebbbeff941c7f862e8c80eafb72b1e8 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 7306e3172929c00c29ca1db360eb4ce82066f237e9cf6aae368d1f531620e9b61eb64f5b3e2b735a3b565587d7e955d052df94a20e4aaabe493dba2c18e85fcfb65df166cc48733632d165129b112598bf5e4c58dff662e558e5f71b25f36708d3ab6536b1cbdb5aa2ee56d9e019a9c3629185b188af909831629ffceab634fc +A = 6b31ef80767a7693e7d0a9ecce54beaf5848120f036923d80b7a0245aa6a46135e32314f3b227268e0bfa1f45b4dce83bea890526c7ac3efdc8e485189ce2c51597c2864c2d3664584be23559c03670622a53edc2c17b3f1a92640078ec35189dd7953e55e4da0290ff1e2996d164d69f1bbe6f5285ae89209d611a7d760e413e23285066eab8e126c320bb6130a91d67ef26d4dabd +B = 183f06828033287497322b05ac08f62dcc5fa67b7a10c6c5a319c9a1e642754230c6d9809dcfd2de4bb9e360d6e6e1180f6ec6e0d4c6185e34ed299b6171e653521d0f7b8975ed5e7d2c51d27f9784a4b6f9b5e97379fcdb42e4df981462cd5bb9d0501f93f217d954f6baf70343ec710065eacbd2b778430ddc36a7ef0515f29d5fe78d8708d8ffb6c3391c6f632cb1bacb4ec52972ce0a5 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 361ce44d153f4d251952c0b90681a19b7d2d8df7a6c5d459691a80c06107b2e818f93f30f8dad352d2dd87b01530d51fd1c67cede9b1a6167697098e41bdc5dc5e7a3c310116aed0c7b5fd99dfcdb3517c13daaba6ad10879f600eab846cdc110d392d9bdc0e8ab34b317840a725a7a12ceb48c75e8dfeffe2947aa85b2a5158 +A = 1e1f2e44bc7c79a00afc3b2570d5cd27ad5ec9f45aa94f63f2ec3fa6b69077480212a1cbde25ded7ab1c6cb1ec26d5905948e5c1d6d109bd5047b1e038666054606b42e880b609f6f00a219dcfb504d481d6fe709f4362940f6c4b6f2e05d243722cb32bee5508ec94eeebb53b5befa551d3ab5dff9cba3daebdbc97179e56cb778aefdda6a0c24265728ff9e59ca3c2d615398d97e66d +B = -e018708df037aa2918850fabcad82731487fb812213b1c067d0688462a4d518e5ec7c4c84f2cb2017aa6bc960e2faabbe361ad8f66355366cae869d366f06d7cc32ea08dc51631e7f36a4c775611095d8aed06a0086d0a471749246d7157947a1eb5d5503f207723a7062382b3e45bb84c6f555e48f6d63aaa1c04fe13c0108507c0ced669a5296bcc16debf18e03c32eefd177bbc1dd2f19cd +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3aeb3ff6e797d271fd2271499a740a91569f300d7392a7b5898084012a3c5ad379a57d5169e43089cd58fc7210314758d5368dabca2f0ec5cf6786801bc99b45cd60403c732d9f98936aed76da724bd3e7d4b622dc690778f11fb0310fd4cd980b220627f7a864e107f93a6259081c6581e5dddba4890508af8057c1af29a745 +A = -75e06b47f60edd23148c3736c9c125a617beea7c8fd47e662c9d9be883ae925b7801a0030df3f4bdd3c9fc386f18c4e002e5daf4a6f7fa27b2f71252c83d5f1695e50d62a10b99e1900987b342290decf681a064f789e11bc3fd75d64e2e78ace56e7491fbe0eddd6f9958a5f95775c920ad6c051ebe7750fa76891ab00f42c910550a42bbc1c1e5aea0ae13b7e6f916a5d228bd57e854f7 +B = 434c8e4767d0d7df2125def75a978bb1509a26bf8305cd03df748c6c12b6dc580a2c1ca9a4526eaf3936fbc4ec797d0733217a54ffc9e1d7c6ca04fb39679859d5bd3fa64cd0a09cf1a056094b9c20ddf1f00e134533ba9892c2ca7346ac8d0655250eb45df9f0b7983bbf71102c6f1a2d9497e7a45eea7b3095cac037b7aa755beeea8a6191da268780179a652d94a732a2a5c7b626c0de3145f4 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 734a429c91f5b0f06fd47725ded06222c0193dd407e9daf136696f203e153c9bf6df59016849284cef93fbd35edef2cd31c9b956fbe562d2a22100f177254144718ac7d22c99783fd523b642984794bd7beb0d0b363e28d3f3469ee332ee364faaafef25c1d4a11b5e517e44a412ba717a113ea9e1e8f2d6db8fad6f10d06950 +A = -18dcd213e9938fe4b6a64abee3b9867f65e47e5b0365d45a8dee14ddf787f34072ce32f38d4d48ccad236005a23c5fcdc02b72cf27001495663fc56f428072d3f1bf5e33ab2c5f9dd9facf122f7225ea03c2f67321530a642803f65a2e9428f32d0d974e68a25f705e4f8140568f7e4b132942b49f9ff53f04f241feaa29aa353925fcade33a0cc192fee2628c2111da1e652cace9d304d0f1d +B = -2e5397658a5e6db9d30f09e93e67a30dc84b1e17c25786e041fca48ab710e1d0497ce615264f1abcb23d5aae8412b58430bd801775acdce06cd362438898697940712062b611c92ae6ad10da31784207c5e7b9362b20d7254da0df8caafe0736002dd466d76b1a03e91a8dbe8a71107abd5f07b00fcdca2017391c7c3263881a3d02a89b0e16a2a765a32d24ae6584cf44a88975c539402db9a301dca +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 427609751f28edb62c717bd98ddf999cfcf65128b652be1b5aac0dfe1bc0f7687c580ec70c8290455a9448c69dcb550c0cfdd109af561ece2ec8707c1d02e8097e780f32ddd932e706f81f68711acda0e7610f4dd0fd55f6ac7ca3a3184f655b0b29d2d62974739b43ded96b413b9e3f0033ca1edace24b6bb610bf06b5d940a +A = 6576c31d48daaf7d6bc3658952c4ba18095f1a0d73726f6fe59381af45a2a6b592adc79fbc3b597e1eea711ab295cd991441fb5fc4ce5f047e571a7d949c709e0d31156184be4b8a6a49691ef93d7d3b120193f6ee82246aeb896b8b7b4c74c27c02cb39fe0335883a3f088a71ab42b947a0cd59dd2155c65a0274ec0836bb8c2fe394500724ef84d869bee40291363389e7012d672b1eab6696b +B = 1ba2888f30be283b588cddf00eb3ae3c641e35fc0bb3a9fc85d7fac1e81052129f499afd3e8458d4cf893d51fe4a2bcddf70f28c8edef16c7bbfb791daedf1a8248faebe36953560498af652d1f1c7aa0e9a5a667d9c94f7d9525cbd5a82147d58b738dfbba5aa162858c2c66d0dd7d8db38d41a2261e6efc7d0c8b2dd2d6962be0fc796705cec8e87a13092e4a3febdda3d4dbed9d11a1d5f92d7dafcd6 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 533d6d8d7384e6e65569ba0daae0a8cffbec1d20e417a6edb42d401a59de0a91a7e6854db081ce33b76faa63f6d866993c245e69ddbe6c86d339f7107a4807856cbca23cee2bf5496388ae8fd8d7c78767d0775acd7bd6202dd75451b424034e2766185969b5663b638d539f718e50a9f752f406c224c000bf1ae1fdd60a2a82 +A = 111940235b144a42a13201a41a3f9e4ff02948f8e9127d9a3007906988a50b36d7622d1221155f2516812074a7888b1d8334a01c02ee33b3164d761d02b36729c299ce2455a462bf18471fca42e5b01615d53723c3fefa5aaf4a039a6caad35c348a0a4dd3f0204f084f35c0b93ab233c4066dc50c5fd3897a769a7c5bf309f7a9c30e905466c8394d509b79d62a69b58c73d8d3f1665ecd9a8a4dd5 +B = -e2633e43c38c0b4b8713c20bf4e2b8ccba680ecfc1139954fc42724277beadea438596942fea1094091671c2060dfccd0351b2fba8cbed35dc963cc18f8e8835052da884799d88ec1887712000a0726b17cbc4302421011d5be8d234440eecc363f09e2c04bc9cded3cbbac9a5bdf0b6d418822fdd90dead20e5bbbb3566ca94ab85f3a00d32842eee6521edd18b9aa6872340b2f47deb961f58bf231e01f9 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 33960d7ceac73f342d46275e04fed56563decf2fa4c0e9307c90288e911ac8782f8e1354fb051a9da8e2db83d7c710b5d2b611495e72ed42259ce783a7e7a8f601c07061ec749481d39a082f29dda1f9c7f444a33ae1c1055d37a677b848af371cd3bd41c851d31a07e144d7add66df39576b8200a8b918201630b3da8e664c3 +A = -402034484e499a8efd610200790d443c5d3be35d19d8808da85954d42dca3f24177de48f55fa2efd7e4f7f624d806a8d461c3bbe0b626fa1f3cad2145746464108b367b13f3537ff395262256bfccce5f0414e1f98b59ed29940171d46ebc4bfa1a27802cc30d9221cfbceeb92abdfa6e84ab4a54965568aa10ea631e82067ae358a1a93a3a3fe3a5ed5636a0c4cb373b4d49f46f8fbbaa665a19200b7 +B = 78ec7dbfa2b28e268619ba6db34a23adab25e7f8690aa9464a7d8fb7c6b87d5dd9d33d4c023bb665f2d96febf2638fc087ed30796fe7517fd58e4120c0d319688e67a32bbeaf62a987a9764be75384bd499b0e00a850f27e303f615031299c631844d10abc571f9f2a0f742cc0e8df2fe3c244bd825bf1d9134b2f1059e2a1b61985ae8daf9bfbd9eb24ba268ca58553891945ff1a314a78fdebb5444677ac081 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3a1ea3fccd6f336e6d444d68af1753b83145131954c20f1e3c433a89eeb7e267425a34d91f67fd65191dce85769ece2fc7ab12d032f3e30f8509095ecc05148e47a85391b21a18257c338a6a3ca9816987abc8143fe443342b34afd8a52fff00dda2e42b1b39322bd38c6a1f711051f791d6cad2a47ebd423a9b933485fd5861 +A = -1869c53f86755aa350115a9f49d6248cedd42a339506b8ff59cb878b7745956f142fc4387322c41f369773ed375b72665026771d4ed1b9ece08f84e4782d4c3b0177853cf9ac3a55f7e52f39c1b82aa42b30628a4fa6a838754ec6ff9809308f675e455bca6f44e298394888d85fee29d8a0c8e9cdb9aa08d68cd70e13a243b5804a3ec199f52ccd462ba6594d856602cf1d5efa509047633923d31f78da3 +B = -2023c544b6cdd8d971bbb345300f7a101f6dd44dede6bfb5f4e6b4eafb7a40728a3063f6d4bdd0f606ddecf062828cf889b2f632d0c9254c28f36dd974aef116b73cabeb2bba98635841c2b4d2aea833e35eb1db9fa9a9d33bf7b51c49a14907dbc6036b027a039192b47406bcc56bccf375fbdf40b82ac4b3c660a43d5a6eb656868d383cebd099d2a73506f675cf29649617fe06097a46de93c13d1e590ef2cc71 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 4331f18a94c169cf0253136bc4eb7480c9fa4401c18db1194371dd53e5f7b75f07ec2e1e1c4116a5d2a8b2cded4b22925b67a88af9b8479c6e821d58cec7ed9f780a4c41e729982cb33f69b87d01c11cb9a8f7952db1920b6eb2124fd5d820555a99327117d7e8e26d18e748fea3ebc17e1d07161fda57a21a70c7f4e251612c +A = 5e7d4ef7d6ace6cb106e38d96085d3f3505983fd952498af3c1d9b2af61e4ba10e14961b339c6e64e11ac758d5fa18c3222138290866970d67d0a4f4e19f453503eb8dfb85b44d1050c86943e7c5d6faf7851bedf7d0cb6b13d2acee25372243591d37dd230907457fb440f83b62395f80f59a2d02b87134887406a78efd77614f3193e517f234434ab3be084f1484d3f2c1f68c67c0d6e863585a8a5ddd0be +B = 114b6e6726433ea88a2ba965f0881beb3ff4d377526e4e099741f069abfaf29e129a1f5fd243c6599f725a389728f755f9cad767ca1d6ae5c8b3a32102e47af211e86d67574bddfa42b2cb466d968f38b47333b1b55211fd9a315acd5ef62cfd3e83c13ee9d3fa20a06b2292177961dddc7dc39abad9ea31ead1fedd3d699f651b656edceebb0bace11bebd0cfa581dad577b8b42f0a844bcd8c8227880876dd7b0aad1 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2468cdb1a26eaee34db3d2724e37f023c8a1788526b3dca99321b574685cc8303c609c85401a58fe6da181daf4111fe8c6d4b7428b1cd301cdb9bf8cb6f33140756c8b490d3b2e538ff294fd6471c4d17b9d9e4adeae0df088cb9daee18e825a368be57af4a096056b9e76b94c8d3b911b6a074ed41082926773a585007752ce +A = 1e6a59efe0b14fa017c32ffd0962700fa9752242b06ffd0b604b9bfd125114d4e0909534ede704cdf1c9e88a6567f4a2989df752510d087d7b7afb515ad594627ece54b8a8e539074386121c9a3e1c12eb2641ded8719e56d42ef50e2f3b5d7d59f8a6f897174cc00a7449d2b91f33e9df07902a95479731a44fc4ebe8048c449bd515ef6cffed70ae78c832cd43491203a247fcfe0a403862266777947fc2542a +B = -8a9d3646831dcc852fecc8e2335549e8baa2e2d82fcb90846ee82bcc715c716d4a9f62be29d5e1531db73c2186a4d2f118266de33d966b78f989600d772ffc55b1364117d6750cef67f4bae851e7e3f8fbdae7b79de7eab54cc1fee56e25d0632b2929e352c882ce78fd64dd0a1473e80b6572f0d4eb67f6bd6e45c7617314219d6f7de5e505a9b395096cd36650d23e8d57d6abfa9faaf0ddbff90d32865bf5ddddcaf28 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2909d3aef7a21244efc9b5b16626e260907ac11f3d00647f2170ba37197e47b9767030195c2f6d5eda717a83a152141bffed2e26777417ecd8e27aed8666698c2e85a414dddd52b07b52b0da7e08b3217fa6a331f84820d21086a4424974e1e8cfed3501eb054242a9f8bf0803a94981b7b81776eca6d07cd50c050dddf81d68 +A = -73ecc8a6a1507fb5dad40677dc6ec75f0d130ea704d1e87b00d2bd56a6be21714bb30202739170b8dd3605f0553ff57439051efea2a97def70a6d2cc3fa2b9ec27a00c1338bbd588513f0f320272b8933fdf6635e585d1e79203efb5c95a454fcd7f33aa2aeac08902107e9bfb29587ce8610d50cdb7f2033c5b726742fa9f7f20b4780cf9244e6abf6b812171a64b870c3ca4c9e898d4c15e9f5b0194ae736c3783 +B = 4049ae926bb52e862606842bbcb4a5148bd1063b6a56f331cf10000c524b4aaa80b3bd914cd697ebc98d68bd3c2bd5c87fac4ec68606c264c56e25b19d118dc9f2eca19bebca07269714f2955e107b3fbf85530b1fe99c42d33031958280b8e8abea5a918a41cc7e6980149ad68fbf1c0041798d2046d7f88a395348b295858c61c2f33d8512b6fe75aa8fbad62e2f9b0b7876ef95af8a7b7338a2d6b25ec6355c276fc6ce23 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 22407e4fe280ff5a10eaf46d8e1f5a1e77a07410cba4106466d703b11764c60124fa355733b47327e952a12869476306926cabbd797fc80b4a6dedfbec0b7718ee754d447825cc405a98b85f1e09ebb9294c4a4636aebfc61af4545b921cbe759d3f389beece3f29c2c7c07691a4c46a1a72ce418a239fdec80df48732627866 +A = -1e165ca7e1eabd2ad1264d5ed9c3d2b687f2db5b507a0e4d21d9e042cd46e93c2444c6aea8491b5caba2d8146bac656b7754b7b1ae0f6216029c7167fd3b1c3ba2e20469d386d8566ebbc05cb51bf1f1eb2cad9dc4fa454b07cc1bcdb9b8f5a43e354c4e0f4e62d52798f667080a0e0a15414391269fe8c92f06da74f6209a3b215adafa1eb6866f8b3e419468e2e5b4db0d0ada80514249320cecf034477977bcceb91 +B = -3f314681eaa4cb41a3feae8467f7d76b8b05939731fdfc943235aa4d67bdca30e64de541d17a8971e829bc0159384643672bdffbc93b3eaded7844d824604f46aa58b1f1b9d788106aff53438954af015a0387268266a6ba262e2fe7a4c51b5af6ff7f918674b7407ce8282f66e84fd2582edd809b465e4401c67e5faaa9e5748c06e3bb8ddb23fa649ccaf9657dbf79b937eb8959aae8d5bd9513c1e601c0e536cf60c4fc3802d +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 385ba217033463cd9cb882fe30373c2d8e8475dee54aba1ca9713a709f40844905c2544ad792784cc8eafbb412dd68de6f98522dfca1c3de8e3bf4cbd09bee4656c4341153b17c98f9ac09411d16ec9880835cae772bdd8eee51eaba7c02ca6a1034c2c5d2d48e7ae3eb0e22f59bf69537ab6f1e49e58a71c64b8934113eb069 +A = 5137226623f4ce4dc9b80a783777ef4e53ad3c2ec648264db472c517a96383ba1173e52c2659a97ce36341a11e832f4ad293b89696f91a051c35bb1db6182260d4a276d1a9b4be848c206899f87a361d318d38b4073a7470c5743b816cbbc3bc1b20dfd7971b11ad4e20d947e352d42760104a5a3cc590b985ee3b5e98c779e38d2581413a2208d31873f9644ec979602671c9da72fa6f66c603c1bb6d8e690dba8bf4933 +B = 13b45d4105e3f5e8e0ba36c812faeafccea2f1a30e2ce8ffad57ffe0dadeae3a23e813758f270423ecda3da083b42432eead7f04842db8865f9f1e2226a3d298ec1895ae69adc55d1d338c3fb787f0676664564eefe46ca95206e81678cf1a2f173c52d809b1e06641a9b467f191ea09fcdc597271eb43da1a9a856784972ce0eeedd49ad363dee882438f09863ba5af063925871c525c6c0ffdca428054e039e149a424c6d1b5b2b4 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 7865f718cb30026837ca006f5cd997c5b917726ac6d9bd8c3fb9eabda0854d528d6cfc10e4cd3f93f6848582690c6a83955072daefc6959d33192fcf42a111650e50776ba9ae43d3d26e0ef2c6b60c3871aec33eda8c56353903e7ae96592fbf350b88d2f56e03f7f327022a2aa9b7c484a000135b85bbaba6f8836cbfc81901 +A = 16978c06a03276fa2e0bea45740a98d55fccc9d27321fd0a5b8522298a2a90d391c06c5c59e7eca85efeb9b4c91d4a1e9178adf816d597311f004ef98d209b59a2d4b901fa14c57b7297861ee58b89c9b2e931e4ce5818dd4006f3c40168bb4d3dbbd059c1f1cc24ecdc64d37df16b8e8d0529247c06f905ca88a5d283ca1b9e6856fbe8115a326061905b369791772a47900974339722d19b3aac16a0bedd93e1e4e4289bb8 +B = -de6dad276dcc0a9e271ad523620ec570fe6e3b350b934932ebbe36dd571edcde968b6590be14326e0f6394c0a2172052ff8dbc3ff15d94fb6e36a098286333768a84fd0404dfa354173d01f98484fb20897c439c48952b7f1791209fed94e9e72bfb3df5f368d420d587ae8bf036db6700f77b130459e9de2a541ed885c69c5641defa9436a4f7a69d2848d0e5d1074f77fa688b6dcc4d4c7de25a3b1b040546ef7f418112127cff173b +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2d3dfd14e7ec60f842d1db83e29a0f6b052990fe8900887dc44476ed3948870c57e72e91e1941c476baa6aa86f76dd8ab6e6ea41707242c46d39b54215bebdb1f28e59d719fde18bea9994610214ea68ad9f2da24e1ad8a06f8bc698f8e76379ff332a2745af472d52a4b8e57d60280e19f93d5be669e0832824321e9ad8e76b +A = -5144d5ca834f7bbb35d3fb95818c1f89ebe08efdffd35993a7691c05aa1b67f6a28e219b27fdcb66e516097c9ef5f00e4257c561b1f94c52c577471cfcd7a55314d3b0fa308b59449a36adc884c48ef5f34753bea746bd6fab2f20b86814c9fe50e8abaab742916313a50e3c390c67fda8e3729ee3329dc5e4b7d3107083aa3a07daf7952ebbcfea15fae7338cd0b114e9ab2f81dc2e80f90abff7a7ac59e3aecf76fab87633ec +B = 48b927a46dbc4e23d714b256084fdc7cb9d4c96a988a71c956e0bf98785ebc9bf22b9d5c6ba0c419e60afbef7b96cc0c4a13e397aa2d2dd7995875d2ccb127169423455d138131199a263151f28d232ff4ae24e316907ace1fedd02a02cb5ff9c831de33e6702010fee2232bbe3c1c193ce792eadcad0c81e7d7c17e49168377b68690bc61f22dfddb17d82a3b993804726037cfac8aabe8548befc52a3c6c6baaec89a392133cd9c45b1b5 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 3f66970f600a9d09d73fd1ff813e977f539d69fe1784b8a2f99506d868418e4b47338ee0cbceed555f88824f98ffed39befb69e8907a5822ef7cd2a9950a070aec8fe4db9d68e1c0620f9eab4ab529c7e69466e325fe1c6c011bf7ab62bfd1a136597d7d5c47e8eb161ea048477bedc88fa30e4f7ddab2cfeec3fd0bb3fb61a3 +A = -1343c391be3f2b72c4b79d8d6091389c9602e97774b18eabeaae81fc0539336cd8c899341cf75fa758421c7f32eba9df474c934642003408b32db66cfa92e6e414b42b1d49c7e655ffb4c80f5bbff8d2774ee4f7198839680175e1ffec0428939653c6697eb3681d0f92634cab1cabc63f423d5a71d65fc7150aaeea74f9e0153923a1c65dee4a165e6a01a88655fbecd2db7697f4d2b49fca2508e2b8f84129785d36d88bcf59f4e +B = -225a0a4afdde6f6450f28736c3ef6e67d67ec6206a63b11763bc6e69b03f1494b275ac504868caa6d56d684a12dc1098ab0d030583e73a2f45a42b8607c0f19031b9c5f07fb71919868911806d210d43aaaced5894e844881e89bab85a203af9ec3adb105e50b4250343ca50c26df14c46d73a22c2e4804d26d44ff0bbcc13d0dc7e326c9e4eb441f493c9743ae0eea0de045e05d19ac32d2379196a165e63ba640ca42e4861caa24c29cbfabc +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 54e95e86e87bc220c8f53f8485402327885be34e34063a1b81e52a23fc3056758cea1c039ac4e513f70ed9d394f5806fb771dca8e342368184e674e6296b9a705c6380bdaf11550cffc73f9f55b9385c85fb648f105f11138a3e1f9dc0a39a0f9755f8328701484d45784e3e4b2ebddb32c9d9132867c6513201116428b791cf +A = 5f1239e0b5dbfefaba906bfd9003336489ffdf634333cec2484c582dbc19b66782ba40942d047c3749597ec4d89ef61b7803d33a9842f0c903461be37c679ca213aea894d36c1e12bbcaa1c679599d2adda9bd23e712dd0d0bd3f91d146e7a04f3e7ddec8b0db7e12377ab32ba241ed1e01da070c1f3ec85efd8387a7b9421453969ecba8cbdeeeaae6ddb098084bcd250601af780960c32f0a1ad7d7e61fb19f40dff1060c5f332830 +B = 1113f145de014bb6dd6ca05de159b97e9736c45bd3bbd8477f739daf79615fe329ce948cab9787838d7daf797218af5ba7925685ea341b802690bc9588ba3e916145cd3ae9d0c4a149637b890cf50fdfa8f89a62e508eec68f9332787733aacdd57ec1f359ff7fde76138d5b33d32e64cf7d252f2bcff14be3adb1afd8da9dc930f5261e6d715ac75752b29f083bb1de7b0b89ddba633b8137f3fd299a7f77abf79781a10d897e7bf2c958a097227 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 6e0160eaac8e1c31cd3cb6c5fb91ba086d033b4b69e41dfffce7569e61770f6629f23e12f0074c47c46653bbba94701ca798e1a242f7c4e25708d3acb5af6ea307b95cfa220f8879cb4cfff96b843d6eeed2b15c8f1bb21bb2b511cefbad0618d49d9ba33cade6da6ab3b846a6a24e35fb36d41201d3b85be831522b9bf509e0 +A = 14f4e24627c773527ed2243c0d1947395aba5c9cf95ae62a48827ffc1477614ad9c7aaea4b4fdd97e3272d3e220601565aebf87928c301656e9edb08d6e680de845615bb3a81c61ed043adb9d708ec1447f057087211673fa6ad8977166a2b4a8079a4f29d48e7fdd6875ccad05d2c219922b814589996cd9642ea2b798197407acd274da30d3ca008fefb40a25b38cb6042a581393283d6448cc69df9a5dc2b0777052566a8608a1010d7 +B = -b4188ebc5bf3ba31cf7c5e100e79806e92ff6f863c3d68a66aeb3ae8385f596dabe6f627f3812d0f2baea319d93ae00de41ab65e42eae7d396cc8fd0a2dfd35f303117fde4db5e8438df0c2b3b680dca538b42a7c844a9bf0d3697fc89ad0a73594627578dabdc214e0f4aa06b40987aed473e7f42d318bebf7392d9c898b4b8d73a94726aef65807b2ff746d4a9aa76303ed7b4fefbab34f5c87c2df82d20457f68289f7b96dbeab581294974e322c +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 8dd91f390c1f85f153f332de17e5de82979755d835398cdf3dbda1ee73c68f8e7565a964ae33fd5b1f1060572bb3af67eec79c4c3e2eb4de118d471f74351b80a5dcafc682bc3cfde642e611ac1d5bc2c49b308c30985b1161c4d78cf7621b503e2dfaceed886befc004f3a729b4a9bcbb8f13791d973bf38fb8101d6b7a4d4d +A = -70e99398673324ee83495aa0aadfffd7bb9c94ee5251fff365124fabc50175d794fa84509f034c2b86d83607789338b0eebdbbf709a129a0ed0afd21c130d94b279c56f1c7c1eacfc6cd13f724a9352b2b37412242a47b23ec61ef0040a8855371aaf238003c45ab9d18a66cc7dab9653b93c323815e5404762d3f964d4654a6995af507bb2db2149eea59acd72af4d034217eaec0be5ba1d23890081a6a234e125572e3bcf68a6ea52d9437 +B = 661d8832671a4974b493e5d71e547cd46b36730f4017e50c5d1a7520fbb75f0314cbc2ac948744dd494d566ba580a2108106b120a797cfeb1fbfdefdab6bd6b2e073f90c77e814cafd0b7f79afeecd59778b1dfee3446fb32139b2311011576674f96f151f896b477c631237995e11e61e715dd8dd38e802af93124c66eee735c472972000cb4788b26752a630ba63b45e8ebbd979f0a4da5b359abd2905f0b7f3a21b1d381cd02ac08e284218ce41c907 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2b591d2c57f6a5484b43cd7ca247c48a1b38319e843257331c8807d499c7763de4eefed529e70d4c144e5e843ac00ee8d106d0d82163cfb7afe528a7daad8e7ed105942d1128a67e38d59325cffc0c3dab9185247e0082e3ccca82a900d917c9bd0f892d4b518a752f8e9d38eab2acaf3b3b59f15b0fe4cb9a3dabe6e0191493 +A = -1896f67485a740720e23e1642ef02742ce5f10a92e51af19e112cc99c0fbddb60d7190086c942d293d076b474d056e74ec9f0c42055d745a57ba370c51ab2b761d889b766cec909811e2b2fd11d6916b753ae00622f038a4bc55b813a5d06e6ac136e81689407de721ee852cd21ea989ea7c8cbd00b64614caf0974a62097b2eb865f46fdb0c1a2e4f2d839066b797e51392e5ebd14dd92630c070acb546dc7438631fef01594878643a4cf77f6 +B = -3a8e2f3b8378a2605f5affa21c4fadcc655f2f8357a3427d2cec0118e55fc2bbc25931259e294d91bde8dcbacd39e6cbc125683da7d0dcbbc67d7c5866f08e7c4732cd4384d9366868370ea40a75beb23b81306303da4a3e26ad357c5c743d0a4ae775a472afddf8f21cb4a1a3350bb6aa71037607c334a0c79468668d3e727cf1d0610e49f27780901c68aecf1d145953e45f5b090855be714cb39aba2efb0f7db2786b331dd9bb8843de8c73c95ab13b6b1 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2f53bdd643b5b22445e2af3667a93de52f8bc7bc151e196c0ab0bf3b4e4dc0e5dae9e507508711a9e3de52e2aeece6aff7fc8a1db65588de3272839390a35a847e29204d3b9b70e10352c88a10c86cd33e067fb530d20a3a5ffe67938c5a7a9218f1164f36a73324adef64da64d5fa5540d29a76a87ce010fb7d73a59b109280 +A = 75e31ab221c08b3bd73bed03f878bf7742f9b36a89bbfa7e90f9b05ec11edeb0140dcff6e9ad1d62cd7af34bb4284b3a52bf1b48a40f744b561d9ece056a9405ab15f508700b14914e4f427ea1df3093497410a0108066e9b259c1a26ea72082b3cf0e3a99ad054804da7bfa0200d93d65354b75e605b47a4e1e17ef851a37c59a95e1b5172801e6ecabf70f1e6e382740998fcfd8a297aaaba7d04b668e3d6eed40358247767323a8393ec359628 +B = 107aca18938a9cb244ad646a37a212859b3dda7518a5827aa2146b47bfb3bd08d772eb7a866e1f674aab7a1c74cfdc2bc6e9ad1a365686213655b2c7b1977855bcd42ccecb804bc01d92bd7d2667069d853f18a0f0661f028955e39f71ee82b9ce6a81dfb2951b33b123e71264e819bba4d0a8c53a1d99964ad9ffb58b7cb5cfcd3e30b1baf5aa5b3cbd20a0df7ec37563e2b32b4cba91bbf3bb6fd1cbfb2fe0f84d720efdf36e9645c7e9ec70442ea5174528bb +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 32d16f7ae2632b5cec2e90c34d191599acd9a1b5f97845595988c1d0d4ceb9acfafbc4aeee9924ce55e109ec88c57610fddc664316e0f9a5e3ed56ea447111c0383ecdf117ab42351b80e72720a4b1d98d4c73f5235507c5b4f7849d5e9b527d054858c0436ac3d2de2704c4bc25de4cc702f5880d5ae34094766938bee555c8 +A = 133a439cf006c753c132a8559ea13c64f598c5f8bd5043b89d04d7ecbf0ec58b225551c8df8dcb341198fb0b487774867e5b68f9058f58b3cc98168fbed0d0ffa86bf74b4fb0d4235976fa86d52b8dc7e82df176d70892954223cc484ae58b6a60459a9a0803ab856ff9699789172b163615e322e193bd758016f634c83cf50403e416ae241d9b1e44add17c2a663771ac88cf8b9dd94622d80d879ae41f0f4e7a1a32a1ab164f981900fc159aa85d82 +B = -fef33e21c07dc26a47d692c3094205bf4efae6af32f1c0f46ee579c1a22746a3663d66f2919f46f973fe558c61264157d531e66bb9ea10b4b49d9f6ad3ad8762a6ea8169a9cfe01d3dd65518c2e6e58e8c88d1b2f42d207399d7326752560cd45d0ff571309301683770793fe3765c1337d14021d39ea6980934c5fefadb93047ef07c807d0ea5625ae0cefd098988d6eb7af993c062ba313e23176e7abdebcc6e566304a5f9e03da05bc1cc58dfbbc898a67a5941 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 20877c7f53fca97f8e484ba31f23dcf51ac0f4fe4c5121eec576e043c6ec5492725f1b9f9ecfa64195f71909500a69fab2e591377cc2120bd5f60d3fb3812f9e80b2f6c787e0081c1439dbea76b819ab44bf6bffe87dffd771a870e4f5502609249c5260f91175fb217a9eece4166540be877d564049389306e0d6b313706297 +A = -534042b0811c9afca04d20d83898e7653f91a73de1e4b516f3228c6d6d9b963c7f8f4c36e05383da90f4edd072a7eda382c47b84b46b4dfa16f269c2d9ad0fc53ed2ce51cd31e4e32d0c1ee21604d3c7eed2deb35cf8df6fe1c0740a1515e4c702a2074ad6c0fcd403603b4a4e2195d19b265958ae854ccb0b41cf22480389a053f71544cf594f6833f3e4d91fd3d9091df0978d04d3922ed72a4fa3579c5fff50eee812dfb2a334148227a0f5739f8ac6 +B = 6935a3444434b0b03d27545721e253e4281884da027246e46ddefb01fa7cf7a9a030581dfe618431a68ef6d79b03b34f3ed598e7c8ac030e2b4cc887dd31664604fb8afe4e71fbc3135d6d3b4e596044d6b615de7184ebf8dae8fd58506286ae4d3b797aea911eb59ada39dac756d0e9eb6a6c767ab77b9348929a00f8e311f639d19ed88c86eb91f0d4cfddd34e98130eb520fcd2b77507c24b6804d3d65d1b21e6f6d55d1f6e92bba0544829687a096be79eaad7d88 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 24823628d4fe9540103ce5f611f8a6ccf18788120280179a40c2636f30a13e5076503e8a4b6b6ffca21da5b0f9f0d85feb2ce10b51292ed069f35289ebf5130972d720d20dfb8e6ee80c3ac598570d38e57ba33dbd75f1b03eab7847d865c3e8e471ccaf302461a6136dd13b8d31c9f163799a3c24c7284b8826608a9543816d +A = -1d476cc98529efe5b926aba3160b261723b009e9b880bdea04e9b5b03f173040ffafd1627b38be8e00840e85d7acd3abbae2f7a60b305256b920c2b25a8a4373ebbf1a0c69f6e74792cb0d849872500519b6d1c190da30c572e26b44590b7ffdb464a900fc38db013feecf909b43bea549e05f1b7e70d6ad879c613293cf61f0cecdba1a6565eff1bfcdf740bf553ffd5bb7d74f7e9537897184c527b990dea20387bab0dec3e32727786bb14975b23ff09f8 +B = -2b6e12c87ad91a2fa878b9245875209cbfef400e637b557c868ccbd6e94dae65f1ef8caab61f292d739b139e384137a747210c09ee6f3b2ceb6dd212e14525852b8c54215191e116b7097f6729f6426a8bebdff86cdc16effa08d932ab512d7265cc0f57303aa5e6fd2afe0a45180557935c230558d02c3030b38ca88de5fc75c1240d25a22fe32c4e5096aad0078d50989812d7dd0cbb02c736fa563efd32d14109c44297cdb3d4fa3b93a2e15bbb6eb678e93e943979c2 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 2c4bc23d0b4b1f79141be9149ee20cc9f1b58ee0a76d5f4205e0862492c18daa20171285d6ff0b600c358be487e78cb5450d151efcff8d53004eece94c5a37f49a15fb2b5f62a79568382cf0a4232407b139e1ec5a9595bee8435b4f138dd72fdc2946b03817e49864812b7b61f179bdd8389791178a95bb6311df0a5c60db2 +A = 5b0a181f07068af6e1e4b715d92c1b8391949a1e3cf0fe0aa49f3333c826f5582615d39ec28b1367804c1ef54f15fb83b3c578ef3ae957fc89ef22a343175df3ef2fd425f724ec1c3363aa000ef624d64c6d678a4cbd90b41cf7d69a7e03dd60c5d3470dbb75228b34d35469847772ff3d74b1a89a2c492c082d3ddb45ba4df6e3f228de6c64913b79679cbbbc36a2924e722c2c640d0c5a0e90ae86b5364dfbfae80df3d75823aa58ac6c1da78e988a11831bf +B = 19567bbcf615b777b35fa7030db7da18126cd695ca7dda67f5146c97beeb20df24ba0fda4a4f03523a0d9b9f85d9acbdb5793ecf9c1f4ceac81299a1aa34417779175a4bddc0e95ac68309da51e4f115dad6fec33a75d0c5520692a38df64e8d684c9304f9e2e6ac6a66d2e16a03c19a30efcac712aed2b9ee774ea28af4f37c45609464289de3f9be379c733d711875216bc223f2f468a0c9b4a8277bfe49c590ebce2e027102537bddbf2856c3b6e9389c4d1f5390cb0f346 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 36e1e0b44e5afc35d1e19e88e75f030569eb99d326721ced9bd7416ea7367a98305354eeafd204f1f8a652a8442eb0823d2e6644e6320933ac481a3709777381dce8a7c165b23aebf31b2ea2745ce5b352acdf0707234c824da9e1af98bbedf80e940fba00c229539f310838bd625f1fc103f267265ac1243855622c5df72c17 +A = 1dba8bd9d1e6cdc117a5a01b5046353084946fdddf2696f831a942d9db4637a5ee76b84d4ba63156b8cbc72e40559a2fe9b8e2682d8ba1db0cea042bb86f8ed71f6609df52526c42e7494f6114bb62263d36784dd55d396018b8fa47fa49ca6e5c76ebb0b00e6c764e36cb3ec75e3af6a2c14dee01fab78070239638521743d04f184dae79d49a2bf209ddeb4cc72e0c94a93a47c107f5369070ad95ffce034c554fe2a8391e67f817c6cab5b88ae9748072da5c9c +B = -849602ea3b79b33af2bd3ef9d1250c507d332e759d428902dbee054fdbcdcdc0a357a51d00aaafdacd696a15a64cbbdb7e1fdb347be5ddb1f609a4390a6f29f79ccdb51bd1f0547d0d9a2780517f8753a906428fd236f8ee1b433e57f2810d0ad51846304a5729f53a871d8b0e14355d24d3f092e50de4f044e2b8aa14cd8a51fbb2ff36b0b37defa7be768c56fbd4f5169d9d4698fb9072cbb0a037c219552728587d7c35f27456c02020f5f9374b6c53bcf8eeaa14be51899d3 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 77eb3cb5277ced02b72368e41f04a35796c2c6cc1273f109336fdfa745aba7c755b6ff3833e9b124d9c78584f6bfda1c94273522f020371107870c288592b7c23964320729d2308bac8813586e72078119852e1d7706d8e15c195486b8d94358736869b15d59c037ba4dc8032ceaa31eac3a9e3dc51ee17706a6956cff8537b8 +A = -6a0753edddef8b74f762bf802d7fe9b38638923ee2d81bfdda354d40df4422e6ac43724de1715c4088da2e68b63c10c90b236d7dcab39b9a0ecbce57628f4c2950c79cc88a89daa20d7a8679232c8ce5fa30525c56011570107697222e0eaee6871adced52ba01a3aea0ccc9901cb3a09eb4db2f93aba0083180bb41f3f9eaae00fb458381213dad01997e9b88f21b0a79ada1ec3837ac2b63611455fab6839363b796b105c3be6106ff284544bda2a32352bbce6ef8 +B = 542c5fde65111ec8a38d76d8c5735cee17329dc41cfd0f13bf47e6d0e0093a129f3449db380ee9a70ec1e44640839ff18b950c8fd89346cb4701ef753e6ef49dfd9bd27d9987e572bf8e68df399cf945813582fa1d33e07be938a7729efd9a5e7d730bf61c537770a0727f6bb9ea6add5aac9267bf910eac1b7d92ab4184734ef8b1d184c292b2b4295ec1bfd17b8a2a2e4d315a8b37b8ff9bf6a1e94a4772267195c5a7ea6f0a0c267337fb97a023f1b50ad697ea31451192cebcbb +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = 660a1f378a23fc3b47f693a347d90640fef43add9729d74546933f4b78a26968cc9a70ad6fe8d85bf28164881bf7a99e8b96683c6f4fb54162c144f99a27e3feb736f0d382d7e5b934cfa835c723191e5692b7672cf6918c4a7a93b24af00b1beaf1b80320b14cf2d1539e3376779872542406a5df961f765e59f3480e1cd40b +A = -1cd74c052e62ee8156ba5d97f28aada75211979b1c5925ed015ea75f693a04c4dd0a705f6a723ae7b79958884c96fc07f81fca064ce2affc70768923bfbca6049952eea3ae048425b7c6ad1611ed4b8b77f7605629b9d198a77a27f25eff2f82867845cc868edee4ae31afc5d022b2ffbf43c14fa01bef8d7cd9d0e58362a0ff9abbf250e43ea5065512cd707791ea4868e95d8fd2357b3b3aec1a06888ae940751ceab01cf9e49015d42371fac30d48ef5853b6894ca83 +B = -2ac904d3632e25a4d536097d80a157791a6aca6eb10246ea21f4cae07aafe907c6e4c726694e14ce12e376c02d326f4bfc02ed539a5b4615a3cf5c838ffa52124f9b843598a3821cf9f1fe94e7206d6a525fad1ef77e7e77162e8c6d3d860d4f568e8f81153dc47f167860cd52c1ca59b15f1eaac6b9023c8b375bb63b6adf6972af8ca62b39f044378b11c4a969f3939d9fed5cbe18c06749956c7acbf963f640a1e1ceab73fc4c77463ee8d1575d018f49bf0f08161ce4f88aaab5a70 +M = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb + +ModMul = cbbeda9c467ca801ec66fce801c6765a20148787dc6becb199a15c58fae8d20c1d391a1d9d57e1c74bb412e1b8f271dc2cc53c3355c83f3e2f00f15eaf0df735160a48e2273fd1bd75533cf94c5175ce67e79fa6c1422996fae36ba288a658a7a5422a59d39dd81ddea50979e933efc02 +A = 7ea551efeccda23622a1a5029e5525f46d5ccb83c28ec9adb7a3e97c2b7d936238c483a4a9bc92fe0e21208d5703611e2795b91fd5019272d255eeb +B = 19bd92c534f56dc4235dfb7efff6d941112d66acf81b079382c86fb10dc5473bb8adebfa53ea3fe6e4df8412e7807aed029694ca786 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = b18a9cd6a0a89578ea773fbfbf642e05935a995a38bbd54480ea3ecea1751370ef95ff5ad0e3203613f0ef6833237d549676a95b720848c5e9897cda82642a2f373951d5746b559bae2d98ac00fae26e5957c61ac1de95318b1b1aa6d5c64a6ceb6575f1b807060f9e2a241e378e6ebd72ade7d2df18d5353db7737caf52f888 +A = 13c68e450e9e091ae45863f6c1faed25906dcd90a43620b1a40e7a506e7a954256bab0225f3678e7ce6c4ba6e3a83c8f04a3491d9bf097adbd98fa6e78 +B = -ddef76382342178fa6636e62887fce6e19590065c766b047073329ea15fbba96f2cf088fa5a989f6ee3f6a513fbf66f621c6ea6ef2fe8 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = b18a9cd6a0a89578ea772021f58ce74cbdd8c44a09b3937b198adbd8e95e8e35541eca26438351bfdcd8600b4f9b71616e1f16cee707c712d40da9a440681f8c8647bc90ba4c68b08ce4cbca458bebd5110222f06b2ca980a2e9419e71064324e8c36289eff9c67f6d5d011e6db8538a54aeff8c20800b0949fa42c38fbabfa1 +A = -6d7e88715e9854b435876fc9bb2d25218a1451efb73ad9cc5f52b2bee929530e6618a858000b3f24fa5f47b5f461c84eca971e38cda6e1f475f6612ec32f +B = 49eb76e4614ac7b0ed3f534811a4ea6da5ea24be925ffeaa38bb228fa117ed56ae976b590d6c9d9a7a8546d8a6ebe4bba771d6587ac44f09 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 44f8596fc06afdb72a6e4f876b70b8d5d734589f41089c510b0da60ade642fd79cf8e705f09910912624fa1f646da596c137f124ec1a327beccba62a44f228f3c0977fda2af631e249b2a4de17d170df07bd812c233a96d17e1e93910267682d24c5c485f99aeeddceb658a7db258a2fdf73eb0266d26b92e +A = -122231b14c249820f0dae625342415f0c6e7f93787b4206b79e9ecaeb09623636730810c7936e17a1eece68edc7c97218efb17c069bc59bdb9681a79c910c4a +B = -3cdaed858523fd55553ef85d018c1097d7b88f6c30060d1e77b84821ca20b5625723c7d4331ccad1a70371eacc7f7aa11220f83f1bf3595650b +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 6de7efcfbc1e8d2cb14cbe4465c4ef71f0d1d7e80a1d80d9ac2d0b161d45fc9d915c54e33131591e8daeaa11ce02404c9b8494added1bd83e344ad4de7c04f626315caa56fcc5ca2ddd4e1ff064a2957afeb5d280477bf1f1195c7294d89049024fe821dceb53c7d270a8b4653e2fc0a4d8a3863a854bc3794753a +A = 47423c4fec1eb6779fd23e3d4070d0a7bf9a946f5610eb469876797a39c58577242daef8c34926f6974089fc595508d9c573d0a275cbeaf37172f10b8c849a493 +B = 18ad789cf09e9ea182eaf43b28b4f2540e533f0fccad325430b73101c00e440bb64b70ce0f2680184aa8caea2f6f6517e9b80285fea8b61887a41e +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = b18a906994d3247bf8a00f20e4b349a500159d086aa863772e71a68f91af9d19e4c021843f8bb6eeed1df708d55047dc8faf219e00d559517632dbd1cbf4bda61651b9644481d052903be1970f04bb4ee8faab9adbbf858324e6cf5aa9384ceba655a1a107210a9497552ba8a56d5e0e70b0c757baa71d1613683707357827f0 +A = 122773509ee608cd9ab3ff6763629a18eae41be64bcfb05122e0b3e112db48c64d2a5a515d96a042850c1c848ae5fd5f0ccc57b273d25bd8d68568cb00bb17b1589c +B = -af398208c01ec9700e332f3e694894c7cc412a73bde8a79e08764ded92f0d58db8056883972c79a0c9e0ce810786cdaa3629baeb9e5c370a5a59d3ba +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 64ef5e7063a1d95226569a27218e35e93d870a19a43fba9889a2ca98ca5c573fa56ebd77f1403b3bcad17c1351803a809c245a97bbe32b45e21768f28c5b11ad542f5e687a17f7811df6c8735e1778e94d9313c19fa32a6703af7ccbd88b489c96632d10eebb580cde3b905f6345a2a2b86a871b4fab36fa4b0dab9a6c1c5096 +A = -7dbdc37a51b601417efdda2516aba15827a40ffc304c523a47c544d5c0bba6c1367a20d8a6268a5c3f723b1b68de57eceabbb00d44185ec4ba7ecdce5d80456f8cfe7e +B = 641cf85fcb5fbacd6214be4b7b06fda1b80f4683c21c1d08311f6e23a15434b42d30a51912898a1c46b46c00aef7ab7663ecba683897825a4b07d2b7dd7 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 370f20360ac844bf4275f78b7fe71ba5db6f0bbabfbac3384c04b256eddaf04725d2d57b31afa48f047aade156c34441b4a41c0b2146790a2e15d13b584021ad55965588c6e55ed3b5cf5c36b780a27c5dfb72678d57528ab17ca2ac696aed3d9abb0ca448d9d5789fe37e632fa9709f3bb924c4ce34244d239a940dcddd9c77 +A = -1a0cc5b07271098a23f01b3c0d47cab8b294794b74a8b162ff3b313fcf85ea81fc99433cdf4450970311e1d5ff81e9ba27eb867073ed250aaa7795e44ba8d4000e879bf31 +B = -308f93984acb78c5dac2426d9bccc2e3ac361143807c7d34c24ef8f8db5e68a904ac8bfed1edf3cc90d21c87ae4d224b8c46fa42eea77797f94aa848160fef +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4c8f466d1d9829aaca1a22fb6ca5bdba885606b9264933ac2b4c18e3afc0c406aa71ee7ff490fcaa804f457096e44576ff8096fb1d2b3c68450a8bc36d1a2797ab8b621ddc91d75e7d6ba01d86e959171fa428a5bb1f26766f94a553c94f6dcc2e0af90d7776ed3d9fb67e842e88f7d7342afd86e2f5d159db7304ae4d204a3f +A = 57e894e37159cf3c161be9c97a946454e43bf09a7ae8e1437570a86c6b06f84005c1463d27d726afd2e25aebb1657eb78957a9a12c8749049d12007a81d766dbe008aad6d83 +B = 16dba5cf077403ff4af47438f5840f65fa4e058c5cab3cb730154ae0fcc982ea097c6d0e75bbd635e97314f33ec7e31f0e41cf285ecfafaf36382b33d5e83cd55 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 29d13ec304f26247a45ab6869720720fe019d6cf370b9e2df9a65828214aeb4f8b17969b8dd54339d08eb99bbc66720ed78ef79033fdce6da33501fa8588af86ec18be4c4ecfe01781f9d1379865100dbbc020b892e77027d1f04f8171ca51fb73129dd9a96568904eb44e19f56f842b223724a9ffe28826803185e4208f0ff0 +A = 135ebb133a0beb909101da896e3aad7e26ea72b23e60802e54cc6c58a07b1205e2ba1fef6eb86c420f011b70e3f725aaf9fd1873b6e1c1cc7005c7c09e55550414875cfe846357 +B = -e8cbf3feb7be7fd12b01d5bd024e47538f434b496613320ad71f48a8972f687992f97e4b69b5842d2d6a4176a5701327c40325e98b27e4c0f8fee5a457d92181e40 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4309b728306535bafa6787dd79e58324b3f86eb5409d772018cce2159f75832b87909a672b8b4b14342b352e76ec5a6dd66737cb0a20b81c5ce222133bfddfea878b132b6f9fd557133973a0b44aa41a01d54ab565d6b9c62da67378a4058255047a95923daf5f0f7adff2a3f06074ab1facd986d7d26cb475ee818199a390b6 +A = -7a63e108bc9790ab687e0fb8a1cbe1e9ff876e7b5eccfbc136ba05fed93412dbc2ffb1ec49518e9fb867429cea1d7f82e2b159b75bd40eb8370e8a54bf0e0ac0ff24aa3662774bae +B = 51ee025b2ee8abf9dc5ebf1a4600131c00ae4b6bff966dae5c49ab5b9017e6b1abd6434736df6daabb2bde254022783764c94e66743dc752c9040563df7016a1581fe7 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = b9ddcb9ab858d2229cbfab87d87236e8206cf5e1a042eb5ddde201d56e2695a3d0b2a42bda6a284fbd2a5b2c2b80446ce88c024137780c277ec80bfa6e9d15397cc5bac98e58c9130756ed0fde58d475a033fd94b1fe0ecc6fd91a8b42177abf3f77e87c0847a4244b9fd4980f3b42c7c955836bc994f2babfdf9c5b43315ca +A = -1f971ee9a7c966d1e82166503681afc280fab255665b850645321f67da8934baba1226e9efb59e0ac4483c8724f63556a213f2224b993e4e082eefff0056f7aa8a3cf5b655e0f72ddd6 +B = -39309313b04bda1103ca6f56514026538b4a29ae258a2a66424abe2c652b959f5c1dc4755ea37ebbfe404839505c2807ebe069c9abb9150205fe35bc286ca12b64ac46133 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 47555924c31f040619681d4a12064790e981db2c7853efa17e4d20f741f33c56d80862caf86bfe0730870b6c0afa9caf66e15047e60256fec29469d1760d5e9b77d79a84fcf7a1dcd0168a59f870f1635eb033e0ae0ac17bdb73da803206d48cfc1da48507cb812bea540daa2393321ccb0d88b57abdbf3a3bb765692a2c2ebe +A = 754d78d5608fe8c7ed8e26a174fa27833a24c48d23f0e702454b7eb578cb107da537dda11027dd6b41daad329e036794de562d7623bed8d9b0e909cb3fa38d4d21a95c5f4246e0b030a32 +B = 1839baa8b8fb6575832136f1d4632f72f36cdbbdcbd00f197fff3cdb88b851cbd74910ef6d43cfae9d3248e9c85662d7fb596ae45a460feaf308823f06345bc5fae8823230af +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 9b2f026b11d0674e9ec060fdb24b45fceade3070db4405b363d53df1219a02a664882819fe602f430636fc0bda935b14c55c8a0bbcc9b6683417e3ffe7f5d58fae229122ac6e42e76899254295dc5a08ed43c79120a5e5e4124b8fa6048ee90836bd2de51bbd2c6b9b53212e913cde871f11bf32f91b3a78575a006da36627f0 +A = 11402b3b1a45d67cde9730062e38aafe1d04fb1f8bb1975f25cd9098813efa2727cb229adf9490267bd437220d9ffa05bb993e45d2f889f140faed3ac3c7b53216455a830d6edceb02e8db92 +B = -d8e011f18bde068badedce8106f6602429fbcac4766334a0101b57fe94603203a4a8975fa499d8a68198aefd9e68f28e68914f920eea1083e37c67d59476bca9819a8bd628b89c +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 3a74066e7eebd9b63a1dd28548be60573c95f29816f3b3ceef68a5f6bb797d7eb0b0f4ee612dca794ff82f5d7461d995b9dcc09649e2587639ea017865328bb5deef17b5283691724e8aa331d75c635d5e19ebfd268fe5471714aaca8b48aeb846f241c1675e18d35f029b132f81128f19028b0a471b3f75a530321135e35fbc +A = -6c5dca3fb7b85573d1c8899868940794e428171e207b5f9f89fce4b7159236c0755e2959d870754e902e9c40dc1fddeeff6364f898ec0dd669283e6d26a612d9af3c3ab04468707bb8a7827756 +B = 5446269bbeb613e69286f1012ff62ea767965533624542f3b5c866cfb569d6193aa603061701992cb4873ea8b766606da1b57d7b37cf52f52bf85b58309387200b0ed36164f30d52e +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 2a4e727ac67451ca9dcba648050a085196460e4aa4836c5652de863c3e2a76213e0f590de3aee8639304c54a9dcd5f7d5d3592f647e3d07d322708e1e26329f4a31d66c7f2e9d482f22cd9823074dd57d14040a4f00ac2af9677a2c98d58ee1e094b1a8c40092e77eae454638bc3655e77441d4f218c637f95c147776f5bdac1 +A = -19fa688008a12cae228c6ac4982ecbc88da248d7ec785bf2289dc9103bfa3a91eb1e5fd6afe9e0cc035d3312e9ba64028fa6a229db6d0eaf8af43d8c410be7c689c3e557137ebd60d3fa04edb60cf +B = -3e8c87fba4a41c3a84874c987acee9f560b9f027338b584a775c1fcabb766700f758c4d451077a9427257334a569037b0bd006375f71223add62eca19b1e26b86dde0cc251e48d3b60ef +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 52e4a3f6892b425b935c6f9d1396d2034eb0331cbc5241e1d745a9619fa0cf0fc521585cb9d6b1034c5fbbbbecdc81c757f768c7a82f6ca291cf5afc98500c579f82ccf0be233066730f738c205c3c188f94b878c11268871ba42a5d950dc8a399887997cef2b6b68badec1ca641b88d1455e6d97a2841da49df7eeb766b7be6 +A = 67df01e34a26e8239c8edc7ddfccc3850f39864ed237d4dd67588efbeaaed1f884105508f69e20ff6a5cfae1516f6179ae6fb515a66ef0a7d633ba4218c30875287ecd0cfeb5bafafc492619942f97a +B = 19f5076405b3c81519c0863d0c963d545b2834343e42bb3c779788cbb46d89be3f775b62f4114268a0ca0e6af6c0dd659607d40071dfe7f1ad0df9a5c53b741c04612158de396e9c96f7523 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 8ac1d96abd2cbcaa8f7e3267b716f675aebd23694d24c112d202653979636d4d47e27cc36f850355cfc5ca16b78cd1848944f8759fbf6b03fbb7eb347536a9328a5cbb778a6bcd983081374a3f543b1380add14a9468358009ec2baa7ecdf13e7260968eea74083459406e8889936b2fb98c8b9a3597e5f9ca10b76e1dd0337f +A = 1c9ab23ea37f324544280d176cc02762db7a39935f1ede9695b53a3ee2db49d0485c6a3742a3b5cfb51f3c21711bf89ed05afd0886bbf61cbd57b23439a8a165484ee8e4c0e1c0ca2b6478776aa2897d87 +B = -e30d28dd01655b7a419d939e3e7530258a667420fc759bad585802c63fe5efbb309cb502babdad0afb208aff5ce5830071c5a974604c69ee47f76fd87e2460a5b03a57ef0185881502625886f +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5df0700adbd880a5730d8c0637a362a9d42c64503c3b9784046b946c2459a619b5bf804a41c92ed6370bba730c7d39fb2e01558f7ec38511b0449d6e9db8df2cece4ed348782ff1582396ca8b3196474e7e5817f8c197c44d771923b6e286e41e7e23c33fcd8765e06793169999544a310f2e080ffe13640b85f21a18fa11928 +A = -5c01fc52e86f3a344180bac284d2376d1bd693f20a46479c77fa57077df62f83b1e81c94e577d1d6733d276f9cf70555b20e3afcb97534e4e0108a6cce87e9292d78b2d7367ff15fb33d2c3289d2a2913b58 +B = 6bbc39283be06382ea91ad6b1630b38f32385ec90019d2ded7ca6fdaa39defbe22585be0df9c0cf613f6f146c71f901adf525336f6573f7f43e661c44b7097f110d4551e8c75449da8fd39201ca0 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 2a01005f1f387c4d8d24a365708e2506b044f86dfc011262d3577f7313a8f51ab943037361bed1858e021f8a46491a5c73284c666eb65cea1392a780219f13d7188721d7d4b975272293a5eef63480f30cc9618aa74bc51f4175246301a46fdbd34a6ec72d5974aa920be5f321a97b8f19c0ec56ba10eaf2e61f2b45f134b304 +A = -108bbd8824e8c16b81dfdd4dfee691e012e578cb9cc80cf050c0ec4cebf71a968732da36552979ffaccce6667e46c29144dab75132cb087681d5549dc5508f3719e129553fdc97f545d7ddb7d3a4fc575ea67c5 +B = -2ad4d4078c47a3c8f5f9b48e10d52d72349ecf0f54abc60bad63bbbf4d8efb185de90e5e1a686859e1c429e30977fca492aedbf084019e9ceb4490aa471776ed2e8a09151b37c5caed9ede66922b7ec +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a1b1b2d33cb610f1b398e03f274ef39a583d13af14b79e6766859b9ca748237b481a3cfd5d490a073e82e3c53d3ff5cb6219b2b2f71927f27ab6f567547a22dd35fb5919e1ed2b6dfae4d536d6d44fa6216d94d26b33f52db06c4ecb29702588b73ebce87569639f786df4fcf569bb07d5379bf8b83743327248c2d71b5dec6a +A = 5bc53b3895cff2bf7bf10e24fbdc43d17d277a982d5d92f17b9b5a2b9ed8b6104229292ef3997591e2e6a116fca21ad5d061ce438f33b7f7110293770f8313077152c7546cd522ef4054147edbe1878072b1043e6 +B = 1599b541c9809779df3ef40971e7a83f21564bd5d6596d51a3d96defa4dff41e83ca6247969a3dd9a746ab72ce21137f2d7ea015ac6b2ffa8a32997e8b821064d35afde3435b23e47cccafa74d5192535b +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4fe8897417446c493725521c0ea5b2110f91a1b5ba236cbb6ff3f52b0036a49fc82274ca949ac2b592fa4bcc792114bf2f2a78a2cb44cb22c6fe7e4bee7981604de47f6da2ed1fc6a8eb32cd9b8aaca0f2feec76a2438126ae6f409645d897769a6d340308f82dbc6a98ac059fca6f903c5aecd668fa838b67300c654d4013e3 +A = 1717c6503d069103f10bb4b36427fbdd2371b30793e492e4161fe185b2e27469fef6a25566d6b46f6a7f97446315a22d1f1f662f912b17e71feb2c82411ed7eebb84d4f594deffee14934b75a845d83761f36141ecb7 +B = -8808f540521c20eefaa037fc5da782c891fdfc668b955eaa2e4edb592e027a964b4cfbc94c548d785d92992abe282d90dd137c4d76419926740ce138d567da7350d89f2e56772d8f5bcc9ca8d7076540fab3 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 8b9311808bef497d8a5d14f7d851567a196a051610246964917a1f9d4f4449357d2411ba9fd93983f6edd76b8a8e1501146b08b6e1fcdd97b6a41cf637b6ff0cff7a2d6351aa1ded93f8fc1cedc81879eef751bebfbd1559d5d0320595c79e3eb1db0951d7c67c663bc57a672faed9e14c7da6be6b0c6bcab3d4d515e51a0b5d +A = -511312fce1849c3d177d42088e55d534f9f7096282916e16b041f66ea90e2cccddab5cec0ba8ebf0b047ccce72da349f420cc28ab19bc156c1cccdcf5216f19ea922698127f090e97444751dd58fe7a2c90197a9ab3d35 +B = 6a5cab5e322d5f651f798aebf43a62af772fa2cc379905e72d253c49be8193a07ae6164f21cf08baff906ef800e361e1cdf1604f454483e10c8b2bfdcce77c12b0320dea63f9ac0afbb86115b656d0198aa883f +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 665e16ba6cba87c646637a233ae04805a302ef4a10d79c5b65b146cbab8c9ccd491faa32937d0ee955dff7dd0ea3f79fa43c133021c8680490b91d9c1d8a8102ab709ada7508bd59042940b2bd3a4f8c195f781313e45fa8d3abda1f8e13b35811b638b2ab101d1caaa92188d2b75b2b10d596ab159583135b0d4d15fcd3d882 +A = -1375af024e9974cf8170801f4a709b4e5862ab7d18464077727bfc2581e557cada991e9484a1acf80182458158c44871e67e783f7573f214ee4ea1f1821a65068f2bbbed7575f03a4bba36b0fa8cb6dc58c73b100a6c4a6ce +B = -2d64b6bd987d496a3c121e89f4b0c88b6ebc6e30fa9d47981b52862551f3b7251a3fc376db0f2d6daab6e6fc5ea8fa10b040d0dce334ee91d8cfa6db9648df907b199bb11b2b5c41c67d72b760c404b0451f70fccf +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 49e9709810d9f3fef159e5cb45211453e7a94878dfdece19af839b89c0e43b226d7cfd46859963c7ccc753350e74c2501131474e3b8e0edcda18583b0392ee15f1dedcb7144000fc7fa7eabcbc83d12983d2ade477b4687d75b723c1a98a951d21b2e8ed95735aaec77e00de288d16422fd259c665a08a34331cb99299ac11e2 +A = 4e550ba2fc2a44452f068860ce2a59230738a7a15f5de0aeb4d15bda8c61ee3003568dc5971e48343d402112d7a86860a7f08f5cdc0de21fb1aa064ee5df26fa23839b5ff6adaf64a4a18c07efb3582c2fc9612d2208fe99f8a +B = 16f31365545772f276d8ac952506bf4033a884edf1ce583a63d8d9f6809e29d9cce3b3d227f839e6c09b459951465ab4570d2d36127c0f677fc0a63975801896f2fd17887ca16ff7f265e2e7adab1516ce56ee1ee9de1 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 89ca20a3fa109a65b9449edcc729fe97ed45a9bd69eeb31d4a566ec1787b24cb7a2c25b3f89b36fef1cb3645b17c69ac8ae243cdba35e17f5738b35278478bcc391add0b5ec42db9ec1eeffa63a3ecd2ac0338db57cde9d2eb9ca4bb1df84f1a62245c4e585c4f20f26c98fa1957df34409a99a18bb442ac14f0bd309266a35a +A = 1fd8a096be30e4435ce8cc604ded337a3d9d2fbc9666d1893c38546c4e155315b536d1bc323c1e7be162bb0fcd58440915b053ca0d0896e99265241f2afd46605a2a7486e1394a07b23f3382cd190e943e596c747b6529b04bdb13 +B = -a3960a51af5ecaaa70146ce55d639005e9b6b9b58592441d5876fa71470ade6d1e2cdde17bb80532551bee0dbbb71a0cb24dc8a129c1f6e28920055d87e9c66be27fc4b425737f36add7d72e39bc83aabee5534637e2e22 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 654d9c55d4a62976670a5ecac3a6165734a65f1edcc1ca81a8c444dbc98c3409ac8c4f6fbb92f122045fef8b7971a276c7dc4eaba21f7be7495394053d4f9bb14b63fc02c8a55ad8fa9bb9aa26aca5c47968ea1b7646ec606f53606d5529ded83639984683b8a020e8ded4b2d9f668ceadeaa8160245b36a819db14e58cf2bf1 +A = -67abdbc70db183b8c25b0664805ada269922556bf15aa80a47d31f215e216673b8d59edfa10a74f3f09d066055c3b9abd5434ce95eba91dd51576adcfbc7e2556df95fd6642a3b7e0486a635ed5699eb7fb285589c887c8659a2b7db +B = 6ad3e854ea57aafb8980f1e99ab9cda24f183dbbc513e1fc92d4e239077816843f47927bac28e41d3f31c9ef134b72c09dcf14e2e9677a430d43002ae70c577d9958341243030fe58a800a068d6b01fd377e61844f0d434dfd +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 74bb23f7b0cde7924ee52e58bc0680f151e6898cc1bd4a2eaaa05faf218b419a19ebf85b0219f924a26002f9251b83506684af659e5b680e05138432ba227977f38a479ad9d1f3cf68a86ea214645fc4bd1a032f995307e9c9ee432e816fd852655ef20214e24522c17799ef41d1eebc6e097b9792757f7fc43124c609ef9696 +A = -19d3e6fd6de9092cbea55d65154208a0c93ae409c3ee35569cf774b8c8b7b1c9dfdd52e9f408e14ea3153073ed8d92746474e524a903a45a882fe46af92b033f2c41eacdd7e3c1ff661dcc5349ed6bd1aa845eb1762f27593708aa185c7 +B = -3d466d29e8c0008ee6f402551e3d62fe044787bc9f243db9252ea97da9bb75f5be416def97f13cbb008fee77f2eeda672bccce1f36fbcd26e1f1299619535da0a3fa3ffa0c6fee82a494efd7407cc770cf46ed1b8b143f42790a2 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 197eaeb8221b431d5fed3d701a175abc146a9fedf8060e8e611a54f8da2fb27d2fee4539ddce1f3481e6a64435f09a2d5012540d6069900a332461471b22192fb87b63221c7822d3f2fcc35cc38feb6b3e49b5b0fceb52b0ccbdb4e1fd7b0f3eef3d582a6ae194c249ebc52f215b568712b3e50bb8e01c64b114955ebac2da48 +A = 7bd216d0acd4ee392258a7341cd56bfb0968492fe75da0c9d935713a6ac883525a4a520b5b7940b05e3f5e0c40372cb11b7ca193e93f0d3883fe5840e66346aff0f38829322bbc1f0a0e63ce5e528ba5b13596ad7ca19d20b2a7c9bea4214 +B = 1ed4805e53630b886cd733e5281f6d2699b3c79da615f4056120165cc63858ed2ddfcfd0af0c5fc54662aad90f26c55dcf70a30d04ce05bdf61028730b900587716e690dc0c6e02419622ab8c115078b92315e7c7a5ffe38c4a404a2 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 40f69f2d1660eeb6e1840164817621dc95eb930875333bc3f62a644ca5910c1080505de0d54fc9fb6404a61bb2c03b3981e558abf9e86f2047c3928599b529ef3d91c7ccd13c1d69431fb9ea3f02b001427cf519d9fd8182219ad904f47b3785fa05ed24cb0ceafd537311633a2e26c27e61be92eefb28a49d7f583cb6e072c2 +A = 155fb75044fc54a6ba6c46972e2f97531861b8d6afbc358db456bac33a44bb0545deea2fc83023c08b7be473eb68accf5b65b3c5d6af88bc6d8ce722c80d5d1527e475905226b01ab9d7b5a6557250cf8be935339db330df2dff92f2e88e80da +B = -8c6016966a2cdea4b2d8625aa367e1d079638870f1b61e6b3c3a1e6281ece41018d2ce93684d1f0088d021107fb595390664c11435c6c0a7b93c2c6895217a89c469a37d3250dfa457b928ba6119b5c9ca5f2d47b36e60e4325bcb4383 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 9b9e6e1727326fea099eeb008a36539f3d47e3882b77d6089032b99c6cd36ad79fa75b7c19d1509b3ff022ef781b6a8c16fa6881f9ee2c4e00a4dbc93a49829622f4ce6ba9c55639656102d81167ab8a5e1fcf14d71caa60be732f1fbc71250256520c7c5a4579c3fdafc39356a2bbf2c7ecc526dacc0293c7578424c939ab6e +A = -54cc11ea9806ef27911ba721f19e2ccb111045711d301863792f0cfac798758f0a29111e3a0f84d294a79721067f50858767abf507cc10ec9ea3eb27a91f06e7f6b7b4be7001b548cb7fb734166bad6739935081bdf6d35d58ef56180d377e5fda +B = 7263e8b9a6f5387f44c55af64b64160efe97ec8a8159e723ca8977bc17c861e22041ea227c9c9bb467faaacfe352b03cc620eceecabb6db2db108b49c69752bd0cc61a5e998ac2f404ad052a51286ccbcfaa214ea8ec14cd9a2a6db56c3d9 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a18a7498ac9194f600cea3d66615595c27a3efa7ea196ba12a80b5f608f85fa72afc366d23f5ca98452dd190b8f86031a9dc097f94a217b29fa676a6042a3aed2355cc8e767d464a8adb888491c8cb82dbec8f117f57c4a07b41e7e6f6cbd7dc25418603b1d1d865dd2140a649c9d52019ef39dbb6809d1b28b3c1ae64fc6813 +A = -1b663403c73e4a9003467ed12766f16354f79073ce89b66066857d19f3b42791eb360004d23e02874254bc6db54662717739eced153944c4776f334576746c5c4145b21a23caa2b2a137498554c7b749efcaf3393c5457b2bb87ee2ca3bef5f191107 +B = -21d12aad97a5c6e639a2ea0a82b1292aebd418567718014465a22b9ac5c8c927963a2a4530c41d5a7a6c14805e56a7092c8716e4767b54a393d8552c5d3c366b39fb3b8667c60e6075e9293bc938e407c53afdd1174843b76aed187f56bb4be5 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 1983576ed73d4d87d8b94cd3f70c149c0273e966176b85fbbbb7b3202e2c843bf1f8f4546ad7a4916ea4c731a22bd337b6177fcd2da8bd301f3af9bdcad800449b57986e7cbcbc7eb313d6512b2894c0cbb6cd753a870860a49d6a682c20b5e883b8c4839b3321aede51bfc42bca163a924191feaf05e196d8dcb7fdd9941a60 +A = 576759af0f02406e8dafa330babe9473d9d970bf371ceab30d2f98f4470f669e042e1708e2677d52cb9f99deb9b53f30727d16c389bb63e71e923475314b615762c7612269b5ad7bcb5108068bb5159cb8dbb8d08de2bd4fa4d9db6cf6e3f5997b9b416 +B = 1a4e34794747cf4aa626e964b839ac497b1357090ff63088f9fd4399312df894e41b395d17b8ca1806baec6115b1476912ca9c4309f00a46d5f7a52c8f640075422af06d6d6d796359132f4955072ce90e61b40c992a155b2bc31c262e753aa7d00 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 3448648ff9f7425937b6faa54551ce14dd15566e5d41b2bdb1a8db62037459235a5b9546d289cc2295b0ed584fab2e1a798bc25a0c114238f61ad3381a5b441cb67f92cbf66007c980db3351adb9cfd2cfc769b5b9b0bd1701425ce1ee8d4b9f438ce1207fa850aaa1d3d1f970aef874c2b2499a150d29c2ceb7bac375009b77 +A = 1fb54cec882c274b98913e76342a9b8e631bf1d381fd8a4f7e0eaef475642ab3f5da70ca2e38741bd0182a959e5e985f1e0e7d737beb8c725c9b5ea22f7ec25b6e564809601e8405a5b1362e7792791f55ab64a57c03a99a8518d7f65feb0e21be619a6a95 +B = -8180d172d3afe00e0423245f47591d5f750f20d2cedd8ba6ab6f9aa24f74498a96c9001a0124c4f98dbd402b63e71eaa3a7af8b0d2fa417fb1d45f64e10030232b9155169153496aa202745a432e547002954eedda7cc9c1ca76811bd902b192f1a1d +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = ae0fd585408a99643271eef575285a6261a4c4a92c1956b1ab436d3cacc8d4cffc07044e57b357ffa43bfa9aaea57824319579c5c3e2fe4dd48bc818178beb5fc1ed60afa08828657d00bb88894c975378b1dfb452a5b88fc3c1d81099644a998a47a497c8a2b12c444fd2a088f47576b7f4fa40f34a208fbc3348ce33e59150 +A = -7dc7dfb753c0bc3ab4d07d5aa78664a7f57d64be4d4780ea81e3efc967fbf1bd1390248bbe259da32108ad96bd8b39f2c9f118bfdc96bd06147f812af831288bb687e4e1742dcd1dbf2b7adc41afa28d07dfb8df8bb2da5359e66330f5c65964096a96b31dd8 +B = 756f3e407a3ae698f103fa37759e90554f38378a9b8eb38581e0970ec8f9c00f8392612c61aca5fd37d1063b78c19e3109f35c0684ce523c634190b3164ef06959cc42e2b77e1bb2fd50eb59c3dccdb6090beb809ecb0ca30457a5c5948328eb218e219d +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a2aa4550e855623a8ed488bb63db8fa4ac374c1ae953781aac590f78a364fc33380ca2806445fca5bb9ca2fc7ec4db5819dcd5769e3b746286c49a7c80149e7fe276d095929e2cac6ae57e8102f7d4c96261ca44cb6f1601f429528495b6c3169e15f9babc5be696074d45559d5abdac42393094c450d6a4a45bbf60ed7847da +A = -16d0aea9c752b2e6e4e13f7ab1f0a2c1776874967b0dfeeef7e00f8d9edd1e11d2aa702be45fffc284c47811c51dcee184a134b8f6d1874026eb51e2ec80c94837af4602cac3efde556ebfff578fcc56c00de99a43638ab68387ec087ee269ca64233eb5b1762ae +B = -3c6b60b0ce4b13a5d6d9ccd67c76ec6b71b94ea7205e408eea099c7ced2f3a462954741d353d0af850b10ffede8ce0bf80b6893288413674504829793d7ae0cba53b163e3f26cd99beb0a9ad540f6d2cd5097beac604b1694a9a2f4c48b28338f9d6a63e75b +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 8a1a8fcb68c53846b3edae33ec070ef5cdcc1346ab3a98a116344e6d2810e2e3f60f0fe435fe7ff257c7ef4c122b3c34c776f4912a9621b6949308e2cfe2e0827536c7464371ce804bd7cac1d76c5bf8b4a6fd4ed56b65434c3fcf0ac7be543fe2d09ac01c564d7b9b463740dcdfa9068d4d8e33f29297ab452e6ec55c263de +A = 7c4878334ccd9e20cb11a643b206626ea5d0b20973f18535cd8f0fc2f0325a67d3558e4cc9cceed0d88c6d2215c220b8d0ce230fd701502b02081e3f6548e58e02bc2e79e4991f8ef188a84b0a367758b4e534b72cd87de7f82a26de14fafd162a50b359574812cda +B = 117d8b1d2a3e2049e6edbb9494c68a97145ac3e658aeaa05e8ecec4b090d5f467cde34e05fa7f5fbfa32f1d9dad70955f22130c358468eb371555fdf57a40e1df398c166a22a9df2e1f4e18590b00856b4f880f6629f1a4296056dc66a29b6f0f25490c6a8209b +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 2cd3de06953acb87b773b8bb28172b24adb283d6adada676f5f4548990827635c51506c85670767828dc5b4b91b45a7ab89a700d70bdba4e0355da32b52c173305767721d18dd2cb6c55f890611e7abc854277a453c7500efc4cd4fb8e6c9bb7a73fe5c77045e715fd35d415b3496f7463ec902cbdc18f9f6f67c33fd78c3210 +A = 1a20ad042f46330df937b879c72ef00dcf39fb85b59186b8e7a9d40723288677ff6ab2b9bce95f34f2de37887c8a9cdcaf231254bd00c7e25b6042695d7dfc05a11765120d1dbce29dc74f35aa1492ba0c5ee65114d9a246b57dcc2eb2ea4a310be98383fb934121db20 +B = -f8ec67323cff9d53499ceb3afd44b28f0538c39dae8c965ea27d645b430c2f8a4965eadc8ed864f2549eb636ec558419be71f986f4c5783d0dd5253738b876d9034735bd13b18fc670438387f84848308d9357ec2aa4f6a453bdd36ff08d54a6800bb41df416b17d +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 1aebe2bc35eb2e449bda63513b1bfb55988cc8e6ec8b3c8fed5ce4dcf53b95f1b438c41e3b2348412b35e1f734edba30273935b03d16efaede429960442a01849c352349e23b4af88de4d01e9ddb53ae900418d49a84b7fadd2669261a574557c4fbd782f8e8f400895f6a6c9679b72983ce01bcfdb641f5067c94694e9eb80 +A = -5f97994c39265b5389526e3847876a10aa3699e3c3762a127d1a9f892180cce68ca6139a6f71b235da26c287bd3e1aaa1436746d983c23c3105c33ed2e06baa1e880f1744d81a80b98ee1f16220940d721a92118a9b949d4da7d1477db8f5b357b3ceb7df34eb5f62078cf +B = 4bb4f8f4f4c8e63238e8774ed61a7eeafb3fe9a6e19cffa648defe82f4846e3378c892d223957564fcce79596151658a726031a6921cdca0adf0f5325d858c048a6b94312ebfd19b803eefcb93bbfaaddef120ec3b8c366b6d978524d5c74218da77e4c3b5ebbc66cf8 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5d64678a32c163874d1c81824d628a1051bce3b55c37055acc47a8630d3fee648df5d319e50b4c56f465bbf696433409b89c07e442425d3018a059ec757d77b3a40d516ca3148010036b003721ec9c999665915a3c442d95ec3c01c232feb201be08c88fa3c6b0769e3da30f1d73b66f98e31f4306bf4e23de78e74743b224ab +A = -178d81e419f0473c426e24428caf25d61b648bbf963f7fb753ae15e5ea3706b53b00bfc8fe917ac9fd6c7096518584566ff71e6d35197f9aa25107a235678cf9ff8ae1501c1d5a15d2a27d39d066e169745e1e8c808209bcede0d732423d0c9cfbea322ba3201ebefc5315c0d +B = -27ed464895b65d9518923fde5caaac0c72aad0d1b38fcb7827d6ad4e0c8dc09e119b8b98183f0ef8d5d1133f3f108e951caee035bed0d48bbeee6d1ddbff5864bc192b84eb8a500cefd223972ed51c7f720d1736646825f95f2f10ce6ad47a267bdd8c80f65d644df158d7 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 52dfb6bcbbc5cff46942d76ba45301cbff76e9b894703a6a7fd1af29d615336372d147c3932589affe5c6533f28d3e6a57ce2d3cd7448bbd81e09a13266ea31630cf044f654b87ec3fa3294eb65873964110fd42d86e78d128bead5f117cac98145051552cc3a86c193d738b973f866d068a8994a49df3fc7c7314fbd9805e80 +A = 797c67ebdc083f3c8b3ddf9847b7f3c2a39e35ce2119f746ec87fd5d86671d8fcf2b4f6d440c43e93f45019032e629879799eb58adea729d43d2e40ede6485143bd35979609a12faae7e4393879c40c0511c886c66a24454e4f9912bea944eaa417c9942f09ddfb227feb14e4b4 +B = 1a599d1cd0ab3614f50b71b93c999942bd3d4cbfe7900122d5083151c71d9e0c299bd927095c5c3291418424a7c12947389bd4e0a3c2fdf67b3f512094ec0ce5b52695e527de2b3804dca2edaeb1ea4b487911053272ea926cf2fb3386dc4b1dc268b808bbcf4eaedd21168ca +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 99bb9082e4537426c61f3b813f8c97675c44ba9ca418960ca6e2464cf61ad4eabb01ba00798463567ed3d829d3f14201c740f19fca623b1e9b57b534a65df0f070a2130489afae89b91003cee432fab11426c4d13b7721e6f9db1bbaf0adc0064b33e4b9f4b795511a0744b52f93e3db7bc9c0a991e4e122c463ff344fe14cba +A = 187a8144a0045a92dcad94f0bae7285309ec8fac7dc864b08914e5a4dc3b1a6bb9212161a18c22682ace16a4bf3c03dbaef088b09844902a3255fd6adc0b7c6397dda86d6ab67204d8061c36ca20fd4bb348202037b249f6c110c31580148db46dc5b1bfffa38a683a27054c35326b +B = -e93ff16817b725016279a32dac247961ae9bb00af890fb49c4fd8cf5e815cf98b58cfa1e3735095e6034c9a2f2b5d8030ab30e2271abb45b347d755cd9ab5ab5ce37950380cb306bbec42b6b8056793a0955bcaeb23e2d6a9548684030566eca2d34c458f224c8e337cb8e3c252 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 631f53d02c031f592b3dfaeed106160488c08e0672083ff195b22a2c0b006f11165a245acad6f35dfb15a871a9a2b45c544111f71f86c920b42fdb6551e56c55199e6173c00e27c9f47256349a80236bcfd3acd1730f823031ff9ef594725cb9429ea183a7fb2e03124ebdd98d435313e43819d995c4fe81fdd4ba718aeade94 +A = -72e20f1aa2b5f2c4218fb9e11ced3f45a218f4c83a2017d97d0cfbbf227c9082cd43f939c8909e52c8795cfaa75d80392d3649dd85ddc35bf1cc54ba389bed9e9dcf867da1c05eda080274beb6b868b54fc85e12ae127dcbfffeb043f9d59333d0ab3374c24971e1bc7269450b418c8b +B = 61cb021a3a957703d14061c21d3b0fc19598e19a17df9d6f2418c76d4d37b3f62bd4037aeeb1eda37f83df44c440f5e49924cc72ec5b153856c6b621350ec89d98859d9d1ec7ac4f0c418c6599674322e7d618c5ca588d5a873d5af356d4771c6cd375f5dbbbc69f50b982b8c4d1ec +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4654a62d9491f28599a976288cd2068d8e3228da12f645413a92f482efc66d1737495cd4a4c733f147eb5414a2ef6266a116ce264491a3463c9df1b030d83b315f76f3bef8cbccb5c538478a65092547b91e991e6be91ce4549c3a6e34aa7b466e63eb3b88054f6714083695c616a078ed54e1ae46e00f3593af845fcd0ff51a +A = -1a342c154aad619e567fd32e7053aef8d98335a4fa0e35bf06acd7998c43d821de1076dc1fb67dfa1156d7ff30203ec736384a9aa7f5f08cfb302eb3a2a7179b2664094c2cc0df73fa05bf2af24a62b8e394fc76014dd83b434df26f8a67a624884a0b9b4f08f33e9828ae64f5d0c8cdc2b +B = -2c57e15889c3dc9c94361c17585d506933a72fa954ce44dda9f5e33408552ebf49cae87bd0be35197f887fc6c7deca1452a4345eb67d19bd2e7d3dcf651667a8900388e4d5ec71e9433e3b01d2b3d91bb94d0fc3c51c70793f978e4b5ef93a9c6356c0b2f7accb9e4eb457a2174b50dc6 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 6124d9ce4de2880ae3811836235d6d89a1a4b710f1d5a517153ed7729dfb5b56b0ac10a4bbc811db9b26465f03cda355701f9f28c5257fe288743cc0789cc54a8661f46e36eec357580b00a84f1d4c8e3d689bbc18242f1cac30a87cb7a47ea06f80d7c5633cde4c8cd8a1a7e27acdc3a2aacd608cce9e2efe7864d41a56ceb8 +A = 7b48a9663d914e0225d7275e965d866ee6649d7267474d5336d28d54027ffe8572f4aa26230dc7abe9957d211e6c2c8f3185cae962b878cfdfaaf6cfe32058c299247f372ae170a1f7cf71380787f6e90995da9ca5a4be8ab1ddfa8e6e5dc65b6f168b9b8e29e0257e0eec853a6e1911b1afa +B = 1fc4dc77f4a18d4406a4ba536e500aff68d133c6e7725717ae6537b527c6f40f93202a2292522fe7d04e0ef804d1a7013b04cd3d88462fba31534770b56d2e5672e8a6ec7a723186024c40b4717defd1433b9967bd692ef81d5d4e39ba10a3223d250ab6e71d5d253dd0a732ed386ad57e54 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 6443de73e1c826c90aa36fd7ec5d0c3324c42058b1c35d3adeda1685470d363732d23cceb08c3f973034c24fe65506bd33dc45d7d617a53048dcc103d3d1b4fd0534586c2fb7489ff5ffb98303bb068fc14b1bb6bb43f763dca2c891095e613bb7b6920163aa6cbce8cd93d9d39f4512b6e0b28d361ae11cf76037eab4cbc819 +A = 13f739846ed2c3aa0a1923168cbb46f4f0a2f3942ba57bfa5c426cb4d4b3d80d9530405a31bda329a1814c560d54defa3e03fc4f808606a598607783d539dbb1338d5bc0c2e272a7ff6ee6f93e1665d6f5a0ade30308fa047db086646c763106cb875e014e2c18ff8837e4d4d86861b85a5b7197 +B = -ba019333046f76325fa9f258006a7c10d27e89f6d482b95c79296c07a65b8e3bff4a9c9fa7e5d0038da129390ac851f8c0651dcf655a3d4164a731cd20a701895c12a906c732906038a8e459aaeb293fda21346964a6d53fa3e370ebf43c7ec8f66229405095c6a509d0fa15dcf45de8d0e901 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = d3a6fdf4a26993edd175de9a0f012e1eb15a5a1c4dd2741dfc6d0f9177cd5645508b8ab09c7fb34066ba893c38144c7f2ecadfc2b0d15728b407e5db4fcbbaf1871580426400433f14dceac43d28f03376e791b7ad01a112981f29ff4b66102305f0ecc4fd134c2cdc79a5e9d9f085bfcb7e6c187980e68b6c7639c12e8d200 +A = -464cb16fdd395e32fdc613c63ab4768f8cf72a5b74a0a5b0cc581ee4aad1972cd97db7966d3124e30c9a1c80d85c46da2d36eecd7c3bba5866f9eab4d0fa55b2d440a311654466432c681372a80a7896c9163c12314ac51f652aad68fd9012dc63fae6c7673c5da8faafcfa1b4ed5550f2baede5cc +B = 40389ba4d2f5fc152308c9e8a8c36258c770fb2d03e6189b96c4f8dee97ccbe426cc14595c8482e9e22486b61fc570f0e7aeddad2f4e3a480d4b75d14294a3b912928da5692043bd98ab88ece87a9bbd973ec82f990c0ae6091245318c2810187d69c38fa80e835300ed06c0723fe475f3fb22de6 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 8a0f9eff3a210912828fd7b5f2d72479cc9ccdcfd3e8d21739e301de02dd5c257c7ce4bee2def06c9d0c90d5a86bc45fa9f31e456d353775916b3d5684759e4500f99ca1f91f6767a5e2f4b735ae4b756d56c358a06447fa2c2ccf0ce667be4ed143e9e1dc627a561d92ae53a62477270a7944482cbf671138bd2a85fce92b08 +A = -1da555639228fc6ead68049d836d60a4927ee77472fa0ffd3c787d55b6067012560f5b1c2ef8bbf6119345dc6419444c675c1c9cd50602a93ba3718a5b3e1a30bc108d796998b24474cdad19bc2960b295fee97e03f2ca7589a3daf35bd28eb37a67b5d2cb35a30998d5f8622bd7e6b7d3fddd1ae9670 +B = -291fea1ae6dd1c66c62ae3a3d22904f4b4adb2a48cb795d50074095345d661a033f67b20c5d7231236dab871892deaa9458c235c342bc81457cca3f014a75f5124ff4da005dcc1108e75527528e5cc9c051a97fc6cd202bb9166f9e72e366bdd77c965a70592e5684fcaaf2e03421a2025ca190fe158 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 50f4d25875150bab63e4162265a632109d6b4743f9d6b55306858034732a4895ffb3720286acceff287c38320ee9945dcd0a1bbe5ae1456b7f36337cb7d22b679a6821a450765471257d52b6ab7d59a763e75e9e64581a93aa54761f6a760866d6baf186cdf4ad2b1a6af26a3e76cdc261d1f07b0a7122c8ffdef595812e7208 +A = 78a1609a7f08c93c9bf9090ca7c93459aef815719b5dde5f217567a9f68ceca05594f6ab17a4666ce1c0c4434e0f4f38ca1f33e501d6958a10da47211cc011da219d4373d2bec4b7c6477b1ab3b00b6c45279212db39bcc11d1e7ba49916c4271adca7eea531adad509ae119348f374ef1203c5af8bc019 +B = 152b46095d3f8db5e6e1a9e3f35c085da00e52764b261c3aa775ecfcd38572d2e86bab2f4bf29c2de4fd2fb6f35f66e8685714634e1be980773526bdbf9c43b1335c5d59f4dffe1a1fe2495ff9b7a3fae3e53e7c3208968e1ad1dd1dc8cf2e2415cc76dfe5df9e2e1eb63f7c7687d539706502d56247728 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5a3ad8d6f1b0763b77f5d40169ff0013de638b459e401f50f4cfb505565c8a4465e28ca1bf988071701dbf52ac456e01e170788ebd2b7cccb50dbfe1a65a89a8aee18b3c11986c9d6e6571f964f376f322e10a1ddd9310bbb40f14b0680385c40975aba43153970237c535c6b0e2cbf6bec918a8fa26cb2f69e98d77215c23a6 +A = 1d5c14b0b51cf31e9d97b7c49cd26097d40454978663f8a74095fcbf9c63e533708befb1a467f94cf599a41220ce13493a273fc30c49275412c5205db712d5e1832b39e65c150c3a4b251e2aab853e4ecb4f00ee5ce6982ef9215775a33565bde3ddbd932665aae506941d3ee31b3f9e4ffc0651f1fb4a5c6d +B = -93cae5dd84584a2a3d88028d6d4cec4146cc5e350b4d92c52ba2393ab69fc1dba96e244f98e2f93f31230904169641aff30dfbdd3dc5fb1f3489d63aae1efd29335345a79ded546e42f2ee4a70ed932699fad17a771ba65fe6e689664bdd1135219aaa905c962d39531eba3e82c3425c24041e17858cbbcf2 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 61211c706730a1b98c628b3c8cb070a42e2ccf9fc6302bb1c2960fb165087f210e9d93416ad9fa21634a05dd0723cc23b8d2a846ab7c3bc402999138433725e737102094db5792249b4b5b1514a416b80c804ecfb04653c5ab18b0a34d8777f6c2955ac66fef62c9ec2819f0e3c075920f951f86b32e02bc43239d9218580067 +A = -46c8c68f492d8f7ac7834f89bc76098146432c59b3301d4eb70d9861a6e24c7c9073f910108c7b35538a79de10640291b54e5755359baf47482b97af56475211573576e9412ee017dcf961a090a6ffb5cd995992ab68e3fe60b6186f7595bd9b8acf8695c4f7359cb2ac709f032fb993d16a74822b4935536453 +B = 46953f424d988fd20700ea08880e7e09ac22d60cfc294bd4aefe637408a3cacfcd0ea6822a679b68b665d6bebed3506d25edc83cc7154b83e22953f9d91157cebd219cd5177fede28c63a15710d0f92bd9e542a7586855bbe57a94c520408fc920b3f8d65b194af2b2a580c90db1cdb27ec26ba929de4573c6eb +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 50a063fff02f2cdc68edccc23976f4b3db99641073c85709626292b9475b9a988fb8509a6223f0a517dbae0cf7cd39dcf1e8ae75196d9f5008c661d8b5153cbdb9520c71068e4719820bffda4c393032edabacf99339e0cbafddb6042ef887b8c498e87e16b62417934015172e63e7457242b864a47aa10e203f47320f03c0e5 +A = -1740e8be7b4775725516d37ba643fc64203f3a61e6b0164d112af56666ad97afb0059c2c4981fa81d72264f8669db4e50e11865907655b1f669c88f5935cacf1b12c1db63cc84507af12cf0210f990994055d04d93f148f213e3d4fdcfe9dc42117c059897697914e3e3fa8fdbf0eebbbb9c3b9fdaa7efa0c9d5c93 +B = -226308f8fbb35b5f9d129c0f6a2bd3e5c272a408bf32020905acc6d02d7e506191e76a3a2ac47cf7a63e6306b256f489ca5cdf76c7c3eede175ee4a7acedf922955e92599647b69d463cc14f2b178b88cd471b8a1c1512caa66b6d5fd8840b98b8d070e6593136e98cce9643e006b714388768920a79944be36624f +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 747cba0d1cde75dfcc0b2af9072c5027986b3e3917845870c73c452858ba21d6d1615eb71ae1b5a03ca44e22845d5432b368541b52a4bb02498668e8b99dfa2eb90ec1948d90564e6ebc388ee9816e329e1d8da0d3e2b12d901d47e22e8a1fabc37408be0f89e7a4ab0f30a03f7e2ed817006809e69c21104d0efe548165f64c +A = 5fa76e37aaf0eb3d34d4f4c590e02b6c63fc62b1d4c9e172cb0dd82409df87ecb43a1680a2764f62d13a5e919db2db08feaf98d5cb92a859dd42bca1047ff57b8fe5974fb3ac11ba2c0d8e2203750f30650db4b2cbd31d07fe18c4df84a0dfdb30f9e528932c097e89d8f8be6ff029dd970a7d2c2551529455b9131e7 +B = 111199f91b3749f8cecfe90e9b9b6951472cb701beb39d63068c064cbb2a1e1d30736026f781836a52ad0d828be6c20303c6c0bd03ad664dbf6044a5bfb67fc20a049fd37c62ab0795d836487b883768ef7c8f427eb98e5ab6621fece77b4955822f8efd190c417ced398c221215b50e9532a869eceeb605fa1c936554 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 646cdb3ed472a7b4599f02329054846a8da173000eee7533240ade4dba82ee3d7a6a92baa3783c19dbd3f76fce6b5bdd83f1f229b1c71a6faa18602e368f1b0b9f8c62bd8c854844af85c2081924c9a153e27853b2a48147950fb614028e090e2198e613631c95e565c2b9b64a43237fd4052089f9d1dd2c00525dd35fa946ca +A = 1c8438247c0ca376f508ccef7933724df512f9e0877596f7f4ea73dcd824809bbc472749833b537eec01ab23656e9758da22ab8a4aaca1aab3fe8d2cffa6672ca0c44ac029c2ca6c3e71780c28c31b5f154c8dee782f6ba009a69d83b1a3a03a2d6275bb8bc3932a1170470fb7e405ae081f4770b535edf49f73a12ba589 +B = -e365c8edbca8dcc4cc11986a5a901e4ed0adbe89b0ab70a53aaf5821862432a1320cf1850b515177b630e12692cb025e3aa43e9acee0d8ad5e48bb15e9a3f34cbfd39d285127b52dde58751f572ae68ad98692899ab12d35e33652c4426ec60c5029e51f7e32ec3d2031032aa7b6b2b63f84fb0023c81d031773f3652cd6 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 7a3e22f4a3f7ae7512ed73a07abb5ce291bc90bad507a5ccc0c17185804b9d231b0ae2e72bf270dbd60170f34b240f716529a449abea0b3d98ea2890a4ce3d9e2214819aefd070e00201e9f271de925c4ba59651e55174c97a13a30197e46997c6c2b152548111aa98df120a617c54b71f8eb8b0c8b4dbd5251f5509fdb8a1a8 +A = -78a99d206b4f095847e9a21de273aa6c47034c9afd4c081a8e93c2d75f4ae5b090921ff5108c863785c413e2f7b4a361506fb66b7561b8b1c5cd537e90274bddaa4e91ce74ad81c6dfbfe1a34a631dbe455d74ed9d041a9183da3bc469bdb214d2ffe893f89c3ae30f8ab99c3aac4d2fe864b891fbf4f537745fddcc60504e +B = 5c41274e9590c1ea44c113ce505931758f2cef80ba3b10440941ec9aa2ac984b29868bece2922eaa225555dde84a8334f1caede99091165151a39538e5b7390e81df757f521236314239c213e9b874e396a022f04629c09bfaf929a0e9fe0b0c7386b0541446f6a2570491067f64e662d8611c4fd6d1c78a9f3ae69f34d14fc +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 7fd27b6549494c9bc860146a3e8ceee785ca03faa94b0ce0a964844e7871e813414cf3f111da49fed1ede5e71e5539f34173d41f9a17ed129016bb9b04c86487f5def9fe350fd4dffc67b6e181e3cb26378ea15ff9b9ebdf1fc86c072c82ecd8bcdc241301daf1b774af5f90f37e45e6126c5da7dd3753a1e5b366038af6ae31 +A = -1930548d105661dc25a5ee303b61b559c4bc1f2e28b2c40cf3e25f98dfe01a7dcca0f3dead6463b55a5b2e0440a651cc9e08e125535e081c742bb3b2f8955ae897909cfca683a4822896d8a4a7073c29a80571445c6a0d53d2efe4a30a79d2fb5d08c0f95b735a1cab17ba40d71b054c9270ba6bc870e58591fb1bf9dc9b7ee8f +B = -3e2a4c1509494f94406e3843c9446edaf0a6060144637234c6d9ce84d70fac54ed163d77d210bf557bbea0404922c8aebec67a0475a3c7b74bfa2f226403ce987c705c712bb8eb0934c2b390a173c3836378fe71a6939e48d187b27cc7236ac115309fbeabd9ffd0396fb7fcd6d46a1dc683606c757ddc3212f5d2ff3f2e450fc7 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 2078bb5c82a394c30a287aedcfdc5271eb3246be05954181ae4f86ad2880ce674640ecd55c2ee3f4e89e2762139586516a28558481303e3071cc9ccb9a538f887553bf5726f3849fc41ab027fb1c680ce7dee3982587ec71b3760e5da6956d6894ad8c4526d8de953c0e681ecd44883a21f0abef1544fe601743efd3e5eadb8e +A = 40b4ba1e977825b7accb941fe0c0a49936a8a47429dfff53502fc0680d705b9fa0efe003eea3ff0b649998fdbae8d0831bea7f34159aa4c7add6bc7cd56fea97d25fb9a6a10f4572c26d792b76c18ada19b0ba06b6142c420dbb40d66be669b7c51d8cd2a5022fe1a8aef7b60965c0176eee69c32ca5023782c5410adc1b15dbdc7 +B = 1bb2f18d7c8d306bf80ae1901115c8dc3d286baf537b812ce06d6872b61e5bd44f3c53d7f31ca8461b3628b255f85338cc325856fda5a6248b7c476532c1bcdf9713dff9932a50e52a9441aff96092d3fb0fd76046a8d88288d0cd55741083a1bdb20fc6e9c20e82490273354bd826bfe001322dde9a15763f2c0e6ffd2cf60019aea +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = ef21dcee9eadceaeab13287d6e3c9741811f6ea9d5bd111799ae05260b1de2ffbc192818fa45dd7befc3baf6840e3b9d24cecbcb2cb1c3d653c4aec6531b941d926fb6692f548cf81526acd0b6b0289d70dd11ba50ca8de6e174f502eddf47e57440142c7f74f594a9abcb48ce1873df057b132ccce8b364de3edf411089d28 +A = 19d0109e0c47ad45f57b8bb8519265a4390534d2ea07f969d84ad33556518b6234d40d1631be3c3cce6d59b7be14750aed114008458f50a6a84ff75b4ee7e4b826ddcb2d2293842ed29e4e484260a92199c5c66367c402bdff0f1a8057127c6ffe452498bb352802e0005e6cb084663bcfa82783a3d72f3a2a341b8075983892e86756 +B = -81fce71491eda139ed996f6a289dde8635a3a257ad6756e844c768e66746011fd797658184fb44b0e3f3c5600c56238ac7687b5be42529d5c9b97c3ce10f3219e1e451bb2dfbbb44cae0828ef894eff3b52b8dba4c115c3b471984441045f2c2db426cf5f86949d5bb7662cd40bb3b3172a19ca3fb6858315d688f13c17550e700cd5dc +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 8a5f90344071790373044193cc4fd92116248aacf05ce639b6aac4461ec3ccb0805ff9876ef44fa71088c295db14fc820f7ae2c0aeeffca055f8f7238c6c90db706d02f2cc43b4960abe3ca4b6dec8bba55327b958e75c60c5d1f43fcf9136f12481c267481a725eecc403a16aa6221346df680560ff316a63ec8b51dc37aad6 +A = -7a54e7ca04b9a22e2b986e72e634317ffa20f6f4ee90353d559db3f3c1bc6b3b92ac6b364f6c5929090373962b49b59cb5d87554387761164982955470cb45dd00c4a8982dbaae3a1ffe700e8903a4a8e4a21eff9d00fa496d475e0e1a205be267499dacecd31551f8a9d437f37dacfdf5a2754f0876a3e02509b78674e7ea2169c43f29 +B = 652001f073d63ddd526abc957bbb48ca74154c8f9698b988178b3313dcde9acbb19ea11a935184fcbcc31e0117d8d2ec695ac56b5a71614a12cf90f21c8882187428755b6a5f11c314ac8b952ced0f65db0987f0f87e20b82a811599f4160e65c7418af7f33604e7b8952b70581e3e02dafa025cecda970d04383ee552abc620dfb9c5df9a +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 67f903e0e5623258826b681506f3e94cc0b086e262bafaa1395294aefc9f6b6323410a44427010d5e8d8288993973ad9939199b85cf02ae0a09dfb69801536a3fa6af5ac373add7efd25ba5fee6d8f040e97056f9f6fbb45795c0bac94c51ffeaf496710b00bc9ddd8e445261d976168771060c9bd9d83838a84ee9428f59d6f +A = -19c695ee3a4ada840a7e3626e61047c5081867b15843ee9a6506ce45540d23ad25ff23b72f988bf26ab8b98363d9a2997773604f43fa732f59a4b16ddf3a45acdbc7976a1fce01b3dd55559c20acfbb7501730f794bc45fc09b1f035d60413bbcf32a83fd3c41599049a674f165ac5283c42aef213d777ae47eea960f7727f5758146efe5bf +B = -210697d47beb73f45207340a183a729a1e78d84bdde1c7d8f80bc84559c4aa4572ab0e6927ea175acc7a268d05616201cb235e610d1012500c8ba9351a37bd68b4ec42227bea55cef5ba7d12ffb180873ab9d33d09e6e969df99fca728dc12dda6903169acbad38388fa9b001edb09056a2ee2aecfab0468822bca14a4bcdd3a4122290ec5ce1 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5fbaff0ffcfb2330283fe59611ef51cf045bc2690e31f2ad3265046fedaa990b5d5060b3c38f17bbe8b2696e527fd77ead8650d329c2e0c1f3b2f5bec4dd85641022f3e0ae6f66ce98cde1a785bb52eca796ae45c33142e8264621ab447cafe988de926544e1a7036710128c42fe8b574f7ad69d830894237d95a55d1bc7f5ec +A = 482db04e35f9fc1d87b42bc5efe25a049ed924f816e1b0f9c8ebe34bc771e67e26d6057563fd5d5320681e1207c0b0f4b7df547cd6d5be6a2e0f2bfb088f990b0303d0ef263cf45681e0e9a1147c29f2ca5251faa633ca53f6e0b109ba69bbe20c58a76a22789243d1acf128dcc936602e832a20a2bfbfedf963bc1027650f483814d7f5e6905 +B = 105aaf563d4c1d436c6a4552770a527776f40bbb844b7701313c5ada95180160e7cd4b7175ddb943e5a22c910585dfc184b52935f06b12c84b6431395f28af2eb9ccfa66b2ee8f40fd44d753c6a83d67a6f3fe3658fecc7fb2f4a8f357c5d244422e48a33d0e2971059695a59d0d39b235d5194e919facbae7623ffc92d771532b6b0cf771912c24 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a9d204c1a497f350fa1300cbaf682c947eaeba8b3aa0450c1db9120852a2edd2a0249dedef3b3746298ee42834d869e9f765ce987a2aa4712a1f35ed10d0f7ba9cdef938b073c3a526e5bf45f3510c94ff1fb84bc77b08e2aa50f5cc75e2f4da37a8a711f8aed5e92f7e486877229cb4ff2a4d0755029972323c0b51a14fd1e5 +A = 13fd3d7cc9d6d6821d2f2b1c40c8e070bfa85b994ee8f3e0baab544dc71328a1a57b7ee57392ab6d24bd85f9ea0f2a312148fc4f4b22c589e9a265d97e73c7a5b420bee180409ec179c438a67abf37eba61ac76197f3c9ea5edf2d4b8aab91e9bb1a432ef1f214c043664a51ceed1f2854880dd458ca253f09d6f6acafafec310774a672d07147b1 +B = -8c90ecd56d6c7cb129d1c9c26e94cf919c5747450542cab52281d11d8fbfcf9ea797b29588340d146cc40e77dce007b68c0c24356d4b75513b75eccbef6e22a5b88417cb6c516578d17d871e7d0957c09795f9a0f19b811db75d61c27e1827fa2773846857fec020f98444e307d3e52af501114b962ea705cb0cdf815109054abd00810dcc270d7bd3 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 57aef35a3f5388c810f576dbc30d4e4e5a39248b319b7766311157179d8bc1d7ef019cdd8c2c0175a8424abe7b33565afc0128724fa38f0900140b6f96bda2e78d7c803124cec8c2f2d6649afde4030c76cd33394fb386342d1ce97a4ecd180872134fd4e22667a687915bb4fda21f7e0bc9100ed8cd3a6668ed3a235d7b15a8 +A = -673bb11795d9d20a1e4ce8ae71d041705990463964505befce5949f895fa31c92d53f91fbc110df4e789b3f3f01f184c55df92927b8b680cc92864466ce5590ed2e98901cfb78b32ea79bf68b57a14cddb53209e08a7f430fee23f4a1475fd2640a515f8b609e98c760b4301747ecb61f1e6209b07455f1c8a7bb4e20c269e17937f39c6a2fb7b2990 +B = 46beea6005cf96a2acb16f37e357bc8975f4dad502fc3aefb4666344dde456c0ee7ea43ec493b6aecbc7aecc7d4cd107aa09e874ff564f5d59d7e12047b048c1da1faea36a7e2d02d0567bc4db41b54a75110626d13597db698fffd577a5810286ea8bf50625296ee8070419345fa269a354ca2eb47fa3108387f6a4b2c0ea3e779908a14469106eefc14 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5cdb7c451b2950c9d87638857407276959142958b06241b2010a9f93625f9106f065798f79ce5c534b9e5a31fbcbfc63cd200fc1cf10217096aa0194acb9043ccf7ced30d9f0bf66e0dfe27ee2ecc40bcd8de66fe2ed6f8cb0d874ff7b5fe71951412731fe4e19c34bee64c9312577b9e7b2ac08ed15aea753a6cd3e286192ec +A = -1eee9d5d3854db52f9b43698e05d6a0f1d1f8df5f32884a775b25110309c46ec5c7e112eb64b2d7f948868bb9670068779b0a78bfc7e17860ee02692ec6790222b4384b9bd7db5abf29c46261c10d95f503b821a4694c45553e0dbaaa977892b916cb8990ac9ec29ab5c3d63ed77138fa1e95f395b3b233d039ab5daecb0296203166e9386d1071c61cb1 +B = -34587c2bf3473a2c5d7f3399d5ba2bb09be8105a0b9f3d8737d67b03d8b91b1c869f4e223d6246abd36d99d84052ae5894e58288a614a0da8d69f1aa57428632c2b059ba99315ea2f68ee210e65a741e94125ee4a723a7828bcc410aa2dae06ea8ed6cd23f66ccca7e85d2e071055787f230ee405e50d1519377cfe0cab4e5f97b6cb893b01134813a7c2c6c +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 95d0b209654de56bd7d6f74afaabed2cbb3247f449d80511d2d3c689f84c9b79587d78abdf0eb37f1b89f1f8dc8a83f7f9fac2c8cda1fd3fd64e16f5597b7f0a1df6da6db9e828ce7be0e876012bd52f5a74ca73ff8ca4611dd9f342bf77b485305ac28a1f8ac7538169f2bf3e4ff4dc5fdb9dedb97fa743fd8ac8791b8e288a +A = 7821d4b65d529c30b8747e184e450cefb11b5ac5dc77905e6fcd3df64336661c82ea68d588ba616d23df485ff0658fb3376d5276027a40b392f47219edc5ecbf510cf0c5b431b02c65e5f432092f941d32ac5f71ce3496e403c7637f63a23b91e3326d01d2d32e99e0ab265108dc5e7919d3983839b3c7541848dbcd420a594e850e587f1846951852ed76d +B = 1adf5c428f2a95c27a943637758d5dcd7ca36592fcb9d52ac0b7d27adddad5804e3edef257aa51c716801ad0c731e13c5dd000f11b5ff1b69c198f236695c1b2f99c0afffb5d084f80fdc534de3b0df4597404b50c7e784c3c55dfc9753c414d145eb0ca4d07e2f65b63f3eef8d391250a5500ef64d9bf963d7250d6906694e7670f92e3d5a7930f0f85964a21a +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 46914b197b84fa99addeaf55dd803182083a7ae34d6d4d3a55d6272af40a600563cc8d9f6b48110d0521b8b99751235bd5a340b1743497ef1cc459dccf5d6da970c4c3103c978ad2d513298f1fb3e68b24a9c7b0795f47d8f7f6ca9caaab9a9d80f15982599d764f8738217f9158517806fded5f3552fef8b7dcd2e725ee04d5 +A = 1c9f5f2a0d72806dcca92dac1450a50cba05b5dd571c2b3b988d33528d90ecc83444e3ea8df80802c30fbd5a6ec2ad9969be73aba6dd27e0dd2c842b95371d7547768916c0cb036964d041284cd323c8073095b2a8cb8797add5cd80f03595de9d18af8df7dee0d250ea7048faa47ae0131ba3f350d82864dc95e5829b88eeaf2681433dd4d58b2c6f70426af3 +B = -aa1e1b3cfd5ca0facc75e46d872584d55144620f849ab05931210b4e1526f12679bbd9cf00efdbd8863970e2abe8fc9fa7bbd21afa9e364e3c9e32f51fe66844fea4bab7f3b1bd278fd803f6bdbd0d296321e67751a0b894da338ab431871adf1514269ba05e0cea5558cd5691920fbc18237914f3dbe4b253f774e5dc1dc57023c080a3b90a004b809d237658ca1 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = ada55d93c533716ebd8c16e23603071950aa714deb942ebbf77206753d2676a7aaf61673c03a4db69d67faf6273828594d85e3c8cbf38460fa2af603fe9c1b6ce104854e7281757b26589f079da80685aec153fc5fd1a223004cdf30247f8398b8e92899857dd199d5d5c32412bedbf9d55f20e52895fc1dbd04c84cabfe1264 +A = -7d22392a8da1966e6cc5ef50d7409c614f8c8f8e5791778f68a00b4a056d0002707933043d05e48347bbd4d0dc1b6ca32a1aa4bab9992e7e620263283eb68d97af13b90a29c1b7dce39ec0b8a63878e8d65aebfb3bff4e67129e3b3725f999f1ec9ae92007911f2cdf738499661c5b6c9bf27712d0f29e871b17318e95c3d14b2e472cf9e466bea91fb71a493b2d +B = 40279eefe59f954aa8c51c9c214fa07707b1d095f697ca40edb820401a45c472d1d7bb413eeddb64c14ce6144b4863fe9337ae4ae8698db92facacd6a56f3b33129c5b608eafa29e9d92dea620113051b926b80b75f320d7ca3d2ab597168c68774e68c47670458f5ef2ffd4604f20bffcc7817eb09c9057fd9989a6786a7e067ebe6724a89e7d1580f94ee4ed502cd4 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4dcae9def5467526b0ff071003e56f5537852cc0bde9d86eaed2c15e36e6429c68c061e12d321bad12e29626b5013c28f118ee59624ae2f35d2c53bfd89e6afdb6db79f0321ad5c55cab03e6a1a97ff7bd58c760d0e9fd7507de987ed2f94f9c79569fe7f03652cd53c67ebc6bd3c9e6c5672891a9d2ee11b300ed3b19753c0f +A = -127f5ca6924851faa2340c4c8f425b1dcf41b313c5c2910e5eff8ef2faaeaa43305de2b3a65a75fe54c00fb30c0ce3e8007db1ea222521190ff1de6d0cf2e777ed61ce8211dc167bf115a77890d0bd1ca786e967a04f077c89939ce484bbb1c560f669aacf7756a4338d97cbd7f09a376d2dfd4d632bb451f52c03c05762f050ebbf112f8dc5acdd9b631292fd7073b +B = -3bc5e9c352c46449a9155b7ce5478c771293599cd2dda58a962010f1f21d094aa6bee03f9311545e8dc6213f6aa73c08b55bcdf4d1d84fecb9eda35c83eae5fedee75b2d15a003f8a82b2b788ea19f7460fdd8f447d973c950b3b250a3022c19ff312ccdc86b6ab50c4ba627b15968c8a66d306bbdae8e88fe28c1853fdfb3fde92353f46b5bc448ae42306a4c91202f03d +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 62a812e35f46e04b3afa7d26c8fd4eb168b6b64cdc839ebd0a46bf2a3a712af8e97380cdf0bfa8a274f7b73e887bb4cc73c6104a176d425aaf5352f14ee51ba549a6926bd8d059b8e3826b174385d4635b0c36df75a4e7da44c34e51eb82322b34ae00e8c712eb75b3882822bce5a2f2f5fd74355319ebe1973284c690bed2af +A = 71c57b08127a956f0c17fd3c639bd1923ba19bfdb83c0cb9dd78e62b8fe4b7e0019cd0a6b73a334c622118f96fd6d91c1e06d4dcef8a3d0d6bf8f5beb6389226c50d14d3947ce9f24f7e0e6a7befad2e4e92dc9ed8fbb9811d908c03ac074b2a5c67b67831a350c4d548ac70810bb5617d261a045e53cdc48117b9fe86d35950d0a181b73c8cfd35edd31af031178523b +B = 1cda2a51a707f8c4d2cbff6337c3f63519705614c26a489b545b1faf366b705af1d953701b568a684856fd3186c035f878788f7e5dbea16b5e7b6e767cf611452a4272abf2a9c5e72b7251a1ebea5098c60cc5bf649cb70980b97d48580967ffe2913309b6b78cc12d91025ae403928851902dcdaaa60f5b323a1302a5ce114cbe174e3eb3c2fb5eafc44076396c23d53b028d +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a9213cd809d41b6bbfc2123bb84860788ce22d5b91f8e24fb616efc286a218ae9652b42912a58bf8ce596a1b48e4c72f27e52c36be1940f7d2138eb895ee36bbb917a59f73e0b6c3266bf4759ffe2ffaee3f6179492658e0778bb43c4df4bfa1a46300c9da496033142ae2c1e33333fd7e82c5a14686b255e224c51aecc2a590 +A = 1cf4e2d5924510a5fd06ff4eeb94a740e430613277149993004b8de1a2b96ada54b05365f305e896df5fdffd3d7bcb54f9a9dba9689e5ad498012f7a684d083c31d7017aaaee720bbd42382e526a35d2add21d9369f7faa41dbcfe3dae426948a402635771a977e19d5c353ec7c1abd279975f2effc0b7bc19990154b723f2f8c29e606581ab9d3966702f68d8bb8065e9d8 +B = -cdab60f9b8e1add4c54427b638ec5f76b30654d3649b500f833b2943bf6cd5d8647549657a8ff999eaffe413ed87e06267b97bfc1b77637b57f29039235548a7569fe6d4bb16ae9c6cfd38c0b8c73aa60797d0d69b03d5a98314f7f7ee25df8b896ecdfc782cf8057f038b6c3e79c99df52f839fd4eff302ddd1256e51eb31cee24585782a0439da3db2eee79a58f889d8847fe2 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4dde3d63aeeee47441a7e733bcccbd4f2e495ca3c746468e9855177f7672d5d82e51da8e268ac24e8971d802e25d842a16a6b8d76b8e46a7724108c02d38a4830453408ca5ced7093676a1db4bf4c94b9b7a9531ab7c26f8de520bafe4431a55a5f5d8c7576427a0f5bf2081b998b82da2e8e959f2ec4d5141b55e40bf6ddeef +A = -5770ea0a75ff451fc2c86d428f2569884b2c88cb6d9d407cc22b191849d389f57a5765b83adcea21c350b37bc6d750d4859f547da22ea8a3698a5cb6154b946331ae2ca18e7eaace951dcd49405bf8d8a716f7762eb242b8bf5e4c53a662c906c3be89e53ddf7a706ee2406c7d0ac17b54ff259c1bd5a092325938832763ac4caf0232e80a016cd1994441808d8db7e546de3f +B = 7e4246ad4af268695a51912053ab6628969af4fcaf7f1e97dd977984a1604e8c9fe6b920f39a764c27d89f75986a4bbc122f92ccd1860f24677cf346474fd9441f572f769daf834e6a00cbc027e15d6aa7ec2030becad41e1068740cde82abed768de7e2cfd325848f6063e2186faa76982b9ca73ef22434a28bd2e3a5ac477af50f258140bff938d3fa02fb904a8ee0ef3c1f6fed7 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 3d8bde8d0625fc46dec46fc657c49c8ab12a988cec4ec1c24e6f4d8ff94514c8d8fee4a08399c6bd23fb6464a38bb5f249591456c283325e343cc289c85df0ff2c1707a6e407ff7a24383b66ab603b75e2dc3835ffe9274eafea148f20764b8ca30cbe483c1cefd51f82dfb93d7793b3ec19a57f2ba03d884f345bcc3188fe28 +A = -1680dd51d8be6069c86ae157922d55df3b58ee6f53738677bcf7332d6e7ef304ecc7ff7c5a5e1f525459d77202f3e815c68f17f9a6bf358654a92f9f9acb252ed8e9e6a849da7491f26d0e33900541ab67ce966d042607258b4382b8108729a703b429babc34496528f198a7e0f814db80fad4900fbccdfb64908febf5e09805d3a3049c0f164f0bcdaaa9bbb06df8f05309be83c +B = -2c6c6b3c89f6e1d1cdd9abd1a9706e4f642a25738aebbc97cbd60e1f4ad79b419dd54bd14f2bd147b1d8e9bfcf92faccee61a43dbd1a2c084bf06a2ca476b3d169fa2c99794fc827b7f4dd010c0534e7cdd03d00456033ae0203b78a7ed229afcec2d1cb96892eb18898bf53584dde56b4316b3bc5186d97e3a9edcd059d7fe14561eefe4881beb8519c1cb7c3ba22cd2e13d874aab77e +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5b4fbf0445807c8feec7efa3c2bf8dd86b1070638f3c87f1e173ee980412a28847b263a266506e70381aae919ae05d306d3a67a6c1e72c8ccf1c27d6296526e87f0f436c98fd1391f83440b58fadd4fb1905a484bfe8f516661e7176a268660387fe6a7266ef02e5fad91ffa69247bb11cfc1b5c3a88c76b7923a26f8a31ece4 +A = 65fe4d55bfcbba2bbfbdae831aef3dc8c8746e1d04cea174c1d336974d81d026f562225b4a297b1c3b044ccc5dc9c830a805a399bf26c0369b52ab0dd2c0ad19e723fcf9f5de2990ebe5a1266653195a2aefd9a392fd3da8c22c523a362f195babbbf5329018e3b454221b3e77cd0dee79f612f86332b1d104aeae7d8d84ad06b107715bb76bce20220d1340ecfc666b2bfce812814 +B = 12f775dbabf1c112523feab443f6e95d773e8220d66fd87bb7fc702588136a048e17ab6845a9c784dca275cfa445d007e8d8383740b156df7048650f89c5ef1a84148488fc405898f9e326cb8052f626c8881abeb70f3a0f52dd83e3ae0cb82d178cbfe8c393449caa2a87e7c8e2901a87e276b49b6d012f3cbb65641add3694fed3e3177777e78fe375f3a3b378091bb8d2998286562faef +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 4f0af7cb0c4e82d0e6589b24b55528818bf2164d41f58505a2b302a8f677df146f8077945dad3790c323e19b37e3379eb95de8abdadfbe4417f8bf8da643768a622ad4898513fdbc72d3b1d2791ec9ff40634678faf0e17d6e0851f08c39405907db85b74937ac403a9a3a1004013c7bd95a585728010689fcaf63b2031bc8c0 +A = 156dcadeca94985ea8bc0d1378daf1e85ecc4c7f8b6d6c7a5cb9f9ac368a97c07e381004023bc575691c082b5e9e13a02fe813a55e76196e4ad4b0f9b1e089bb71a0d5c94254b66e3e645fea25d69bbc5af266e730482a60105306d664f0ddecbd76d54e7235979aa2d806b809b3468078b5d90aa22cbd2c441198d4a52f6259972cf3d02003dc39dafdf3581638e56d08c5181d36e9e4 +B = -9a54586072d093939ad86df11fcd3337ad7e9e478dcbefb2b89d7555883fe8565abcd5b0a9c88ab135ce5327b2a326db645bc7c0e3ce24f902544675ff9d946abf30302f123aeed0f4e28edc72758ffa760277caaf4817a3ae8615784c81896d2404e2cf47c06b09085cd0ad1ec46cfc1f04d0272eac29e774b30f19939d08c036b185983c93ba15d1d27aebe4a357b9f6a298acca3940d2730 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 7c3ac09486a6fb518b98a9bc8a8b382bf2293e2c1154470ff7961212430fe2dd28697e49256b1ad8add082ee27b6ecc016b120e971665be801b720069d30c0a8c6ea4795613017e8883e5c0d0e68f982c328379d7a0afb7825c553e087b33e9d78f90e0b95a6597076b8ec2c1d375e2143bb778c318ca0680a64072cf9a4fc08 +A = -71d8e7ef13d63b4f417c01ec1241020a8ff4c9b2db531500984fd3e45d22b2bd581894c8a248ed7cc345e70a5698407df8f0e4ac71ed2c0d42122a4f92279346f463aed899253206786928a0eb7c37f2e51e1cde7f97cf9288d85c3ed7f49e62af0bf9abf062d2c6544d83b9d3438b3881e0d07b1fa0f2a4446fd43ab3b4f81fa2cdaff199c87965e298943c68cc15f2f3f3225efad68b73 +B = 64d52de221f102af62ab1e9526935b005c81658f8fefa019bc58e641023fa785798ed0dff8f7f999dbcc2ecfa47d5314ac6676c82170d6f2b18122c17c1e1ec1b9b54e333a184a46ad35b2150c8165f0de19a24b98327715e5a641c1b6d3ff9d247c89c8749e775e6fcf5f967c6eb5e73523d4f1ec12db7321b14398f26201a364e1371f0ac922781ee252c6d2b3c657ef259ab73cb7992a370598 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = cd08b388ffd41d0aa29a3dbde74106c57b18d325be8f446a2d9ae95fa4144037dbd41eccd50fa34096984cb11bce555c117c5568d76a8f79d308ce11043fe2413d37d6aa60c366af6c1da93d525e4b2d79fc82c0a53ed62fbf72c919db8a3ae11f5ff8057d7501f5f6dfc9ae461c308d21919d0de9e31b759d1d8e3526fee58 +A = -12e58708c30c93383cfe6e99ee3c5caf1900a7e610605706e77d8f428fd59db2884f5021d7a382cb18b75ed22528961cf43be1c700c581ceac3877e83eabd860583e6e94f3f2989c179ee5047c82b53d37054c9cb7ae08be60a91b10d49510e9f0b90ddf89f93790c3e18cccad5a9d223c605a6c567550e2b4950e184fd97dd68bf30681d3f9c585365de2cadf36a43f5a5305dae555396dd50 +B = -26ea5079ba7ed137a14d00d413d6f818e911cc183c88764de4d91d7a9b4cc7af3fad703142dc7905992eb8bf489f6d8231bdb25603ddf3c31fda8bd9bc4d78835f9ddc1e6445037f05125cb1ccd92eea2e927297e5eb915d5d965a25e5d58feb8d79a890e6036c80ee91e7469d9eb672d7a8db68905d06f5981fc40bf486575a067d35cf14ceee3ccb79b72871bf8f52b92e4910ab17e5e59ab3ae6f9 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 34714506322dccb91308c403c267f1ec75f80faf3cc4272dff4a84c13eb1e6133af6681387006c61e7e087046b64e7ae74eea8a3c0564a7c1f381e1c940d92b2c766fffdaa7318d07dbeb877943a73b50517b49e5117778b8a60212284fb92f29a9f5304f8f537e88acf8afaf01fdf64773f988cfa9551d6884baa70587ab76a +A = 638b7c549ed14256956bad532945ef9e11a50313172965386635a2fc7db79deb0cb5c157e9854117c17f1509d505d01a0e138d2e510dfcca45b4f7ec968b5214a6699b61b8ac68adf64d5394f50d577a154c013612090e2045462160d1f552592197d7da78e03491ae284dc9faf643805f2674af8652bae93ff230fc3eaa833dc62781e5f74d0f0b90290d51d481b0a94ae6e972197c6e84ad7ae +B = 141f62297ee88ad527fd1e0e09d9ab5dd80e17b32f34a674a27b00d719839701664ccca1b00da2613396cf633b0bdc4482ad3a0c3e209eaea7c22f33706ae44155f527c9ca4e341e651760d1c39f65d5e99e649d013730d2502b6b65adb8a73e6bc734b7d879b430798dcd53fa6c0badd57896cb566d9f1e0a7b3a9161e9808e762ca819330ce9319dbe7f49bd663a9f57ac53d65c6851dc7bc4ee66e08f +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 7adf54c77eaea2a1743bc5011ace45b7651846e77f90402297f117d8b1c0377f93f49e92a2457f3d3debec3022a96c74c166d01b2279553ef518ec0e612bd7b382529184640c55b89255b2679da9cf370913351592de39f804f1724de36db90c045fa644e8ff20627f67d6afd4546f00d7af093f668629f9a06c07fab5654ac8 +A = 19c491d5b55aa25f2e18cfb7fda18ed4b020e3f63244eb9f6c4dfa86eb8a70875cc898e305a7acdd3eee081300edb3e4c837940bbc1927f5ed9f651e46581639e133515457464e9c451390828e5e7e00a688daaea74620363706cb69e02717489ba9ad05774c424c18e295278caf4df4ced80b4cbd20cd631df43f2e16ec0334564d9dc03dfbc7111e4252504fb449d5a25cb13630b7c0c565a82ea9 +B = -c3f765349639beb80f888d9c8b7b335ab46b55064ce2a88180c80ad280c6b7314df52b7e73095dfd82896e24604854a48121353aa1de663eff07882771803010005905896357cd5a56a59f0db0045f1aa2c0b5626e132c169abc64b9893f95932f54c1d8cc25f215a9ef6e4cfdd6dba85f6faefeca81793b2258ae1d1427e81e458482aab87f6563abf435be69a05b195d1eda90146a8cc92748ca6f798b10 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 32ba5fc81a7747c3d812cf036bc0edc49f08824d53b91a65a6d41edfb1651d99c11ccb4c074d7f04e652276ae3fdc8d6eedb72c6e46cbb1f7f4070dc9d179ce3e21a3826f7dd2c27943a8d26b192d7f5c4aee9ba0647e406133e3e89c262d37cf468aa3ab8c5dd1b8900dd06cd600abc6d372d9408497d9e20c86a9a6a4ad9d1 +A = -73958019a5a52357b9c1d954c9b14f51ddaced32a4d7b7c95730697cf90029564118ea168d23a54381f7bbd6718a6b662e4c87410e48ac53b7767148582b0bd6a3d35f488e7fcf2b128e0a58b5d468dedabde4d624f4a82e808dd7b175af0d3658c6df1ac0da6495bc9a8dc012f8de55c2003da9b2d478e1a089fab776d99026684026968fc309dae46a6ef2412039a8207c3084f96b4e38e4fa01d131 +B = 4330fdf00bc6d13ffc267073b68aea7419ebef257d63f8f244accb9ee46edd04fe5481292de69d377ba6b6304804ba7ec0a063b42339e6e37867261b9945ec705d3a0029c6f499420e02a773476546993b3c5e1efc2417f51afcec7145a9c2625496865c11636e285d4c8b053ffe66887333c51a712fe9c8ea57606103fd689dc88f1fe37dbc33ae4e92067c5bf51b53e2f8205164c800e5abd677c73949b00ef +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 69b850a99b471003a56931f7856da357a2254ac50ed81dfae019c9b722b95af16047a0d5422cf7ab66ccd898e85caf0e03e74cc8a5a413661e5da483b3f0363e63a7031bb30626c8f73d6e99e290071094b7fe5bbaf4d303192e59acb5e53fc7cdee78576b51595d9f7a25ccf3c7f8889de68b9deec167778ca27ac9d4c71c3e +A = -1976b3bbbf92acbfddbc05b5d9e7b62a7666b239c1e6270db7ec6dc2929bad1024e745b897840853d14cd815aabb01aed580e1cc66ce37f9d1cc4c9bef8ddd35d28285faa29f2003d2a4623ead7d73302ea9f380f16b3fc06b7c2b8bb4ce4c8b03bfb6056a61c620e4decc6048cdda5e2d3ed8a13b779b8829e2bbab91e9f6b0304b1c08bf8fd85e0f3cd7ee72255e5342e077ababdbb545d7f809bdf8145 +B = -2cab554f7a5d21c499a1025f61e6c81ab0fc68a874bf60470cfac57425a451365be62c380ddd31f6e202f29769e2b6106868da7c81522e03fa6f0704522a5f8bfadbd007bac65595e149f6c585d7fc022db016bab32819049e7547bf85d4232a7fe19084907c528e7eb0434f2e5a375ad9b7d463821bef2f6a721a635252576c176ba42519bfa5d97d0e47facb4426aea0d755507dac81ccf1537b1003ddbb0727f6 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 2ce33adf34f2249f8a2d2e073976cb4c78b71414e027657fcefd56fceb022a06c1969dfafd519eb9e2542662c7647102f5c528734dd005fca666be57b46234123bc3db286cfce07bcbb399eb6764daf2b9aafbc2898a5ff43ddfae849c7549289640edc4ab7c4b9fcf5e159623e5497f509ad6f0270a41fd864c9437302ce380 +A = 509f5d5b160e923b4fdd72f4d522a713d780daa4bfd10ddbd62b26497a2e7925c495afc2abf0ecfcb7980e588f96c4078bde51c7b2c19d86d15bbdad5de72fec2e0a284dd693ce0902b40e54af87ac5a5df38ae6d1d882ea6299fbe6910121ebfebd06b454ec5f855bf3e7cd544a4b0d9a764428662e824e2a6185723534f5e6ad829734347d240c48c2c0f8bd6be6ae8a495a9e383fbc7402a4096b8c2c214 +B = 1a3b7f55307031609afc974857a6cc75821e73a1a9535bd6b8e141437c3fd4a6871c904e22c5d9289df7525ac69a0341d3620bcfc5f04b38ae540e26beadbce0002a8a8bfd0f6a270007e4c52aec2fab11fb2a831b9886997256e4b7e7ad3b0ec64c0f31fb0d637869143712291f5073a5756466d7c82c31e08e09683478229bccdedc2cabb7e426af9025185d8dd5124e08afa4e981236180e0a390004adb7918de6ba +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a81fcf9a18ce476a839c896cc5d9b639fb1d74610e2f618c25310147b57cd77806c2aab90be7be4ed10f0122baf9b862b141ee8e4be5e0c23ea776267f14c31e50b119bdd33f2b41f6a4c43d35bf6f095864593e0d8c0f1fd4656d8371af844d197308bbff14e5a28b7181eb6e6a2b31ead7361e287f3b4550ab0484bf7baaac +A = 19f1ce60ca50bfdf8e02313f1c9a45496720a2ce467f1e8bdedbb32525d762878b61476989c7f6ae8dd29c983ea596e521bd4cbf74dba4d505dd9ea5df423474fa9725d5b65f1575d26ead95725e2a59a6c8a5397ebd6b54123e42bca44781b84c014b8e5d2c1a86cf34d764b242baaad5be285cec72ba8ace808058a0226c04f95eb2b53a828d0ac41e6b40e5a4c4092788d9f7e988752f175f075d545f421205 +B = -b115a1101d97664759538d22154de4b000c008e551e2ab10ad05f12274b10a4cbfee762d232df5188fa1161f37ba61d146e8b95fa715d98e016da8beb0600de65216cecf8b8816f6e7e73e2a2bfa7d0bac74b517b906bbc43357fca69de9cb5507bd95205515b97b3a4d6842f3d7b09606cce1c7436c462f49dd05e915d04ab6fe2748ccaf025bd5d19749cc468d228ba43452ccc479c146ac6d781717bb9966bf3835dec +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 1473f092540ae30de595666beb33e430cbec42d7a28d4f7982e62f58025cdb617cfc33f1e5ab93d2ebefd7345561b81271bdc50bfbb0db6381dc0ea023ff7c72605da26dc7da2b5664d2ad7967426ca97b3745f82528964bb68e70087e14dcf2d71d30fa0d1f7b3f10b19b357e7053fdf22bccc5188c6919eff1e5c402b750a4 +A = -68f280cecc512d51ae534f30aa198cf7b170c346c1159fa9cf158d0127d43e50a8d4704ec54b8b4295dd7f51c6771cb5767fe0c975414cbe6d2bb58ae66a095e8832d5f443498b1ade1f5bf249da58595ebd878677b34e3b4c99ba6124e2b71d86a8d99727a16746469de51b0a61d9d981459a6cebe206cd36a09f00ffce7f532e2c31999847ba000b9e01a4b84f454544b6362a5c093b9abe9d583716f4534f2de4 +B = 5b79684387f18d7de6eec3a63d737490dc2a46c0616ec16388dca2be60adcda11ae13063ede3fec177171a51dbef430f8c4b3f6d297b9d6c020fc44e3ffab891d0d751d033fda813861bc067c181118dc613335ce89c5960f952e5fd28bc72c41b7b6e374ec29b837f1e00271cab646c794579d315260921dbc3b984b86d98b8f8816aca4f16de50657e4102f34d9e29ec3a03e0da06e70f69952339bf2ec4a7e74daca82239 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5e4b3f4aea7115d592bde9bf7c6594fee77372ffb19f7745b4de878a4024f81e8290c77d2915424df20004a7abb64c214104a3123e7c8f230c159ccb99bd937521b433dcfb065b186a685fc40f9166bad9380a02e297ffd6a307ce8d2c8f2f1330447a9c06c327b74f3cfc2e98f3351a8b385bae855941228969d1c29e9da3e4 +A = -11c1d396693139df5bd91825c119d1241c3f57b7ce95b46472dd82081738cdeb0868d18eb7c8ee7808016b3311f982adebd5a2e5f4e201ec4a34f3037d260fe580e771222de5a1a67947a4552cc03c5c59f9e60e25063a702ad3c3aa43f061a22567f938a91f1dd697c3e3978fa11ab1d65030bf327f8049bda745658bdd4ba8f3e34b060c6a2c6c5a8be54c7cb5f6b106f54a37d2be9f674f7747744d4350b3acdf373 +B = -25a65b6acda692ba3330d70dbc3ea4dfe208c0df358c50b7872245a909c5ac19ec568b1a1340e1a094f5b8e7d1e3b7e04bb4df002558aefd4540135d62d75bd5ce959128c1300b9d98429d7369610866d98b22c345e531f2beb80b042b6ad48da077043401a82e223e9e529e7407bfa466dd2680973006d047d837c26a60cabc36a7ef538f603ba19f8e923f168ebfc3834df8f77a559c9e0342e33df245f551bb242e5a66e5904 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 47872b544fa0425981ae17bb450ea346135e6ed7a9de0572ae14a6e85e8319f27cfab778cdd8cb5f93b417d9c66ae0fb7bcc6652620f7f3f74acc2bc9f2c090129fa8315aeec9ca7adc5356484474ee803883ba4695d7bc47c87eec508d16a15150cf3f757c4713de71366e958d6af045b2d282b6ce96976692c80b1e0b6f846 +A = 7e8f55c040862f12d8cc6e506608eeca65ce38e9e8ab18ef7007e3cf0f1c9a0696795bd10f8e1e1f55bb4f4f3a35c2e0ad18289e250571ccc26a961f730346efb1e29fb143ed97cf72deaab19834fa2e98e9c12ae4cd23b9c5ecef4a04c439f7d42e110b30caedc4334372ca24cfe4171ef1430528f7b57bbc823fd606fbd30915c5817e6c57c967c4c404a0847b1455da17effeebbec3f9357358e00001239aae209228f +B = 1cc00b95f6bd3abfa697400c98110725a7e109aa9b8cbbe9ae16327c4fc8e5bc93afc7a94da32e98e85e4fd5eb545192c73007d97a4e84ba64fe187ef61d17f0941e165c9fe64c7b8054e24dad30f92b50d1f526b4bb031e6b1b9058be24884b170a145212273c51692b71bc57ee53176d8702b975bb6ba96284b462da2ce38e12d86b342c7f4d3cd489fbce88a309c7df1121d7bbbaab6814cd1e54953e5cc46813ead98f02360372 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5d193b085e57c3f1e825cf3b36c8bdc936c603136bb782a244b04a79fa713dc7b08436b85ca3b483d2e100a012d6430679b30c8e4101c8f08ca0f9010dc0f27fb37be842054dfdd99362e03a7f55ae58db7b47f694bd35d91a58975ae1f255c41617e773f91c2640f768bc702a213f073682dc761e056b34c57edd85585fe04 +A = 1bb1c759ea94b61a1721ef5680f42af30fa31444b27591a03b7c9bf5b90845ab965339f463a78bddedcd62fa21197c32d6850c61bae195f86e1c7a23e7a20dc618c59ce3a1c6ea6306c0b01b11a36d0fadf8214c36a133d689438021ce7c78b20c85256ec607360cce14f139513d9f3ea6eab067b1ffd0935d7c43419b93ecfadf2c5a902b7c39a69bdc023173bdad574adc77706c1a666d66f69578a5bffdc7cd6eee28ad8a +B = -e8072c49cea603d48f20276df188fd2fb28f8721d578220cef7db1e56379c04a6b372e56a047cbe59ea84ad026adc5d0aa930011db63bf4959f15781e060e0240dfac0e2a2c26be12a21e5650d12140bb49a2a8e0f6a86e4b1eb79d9b8aab3202bfd339096529170cfe3e0c18263128686bd9305e92a3c43e1523f97d8a6a2707773e3d441da162a79089c9ea1e094cd5a23474121188013c8c287965a5e77599f6a7d64174b06cc165e +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = aa79c899c2b9518857c9e4f96523a44607c3f6a97d1f40d6474ec79deb2feadd955fe92d789df4d362c828084559fab56b5e33a971abc5449208d31671c7e220c5945886e33ed1d804c059a8e439a92524a785076f9730732bc5a152aeffb5b9ecf3a7e4b55983016355c4c29827496fd4d7e6532c270cb9ef263573e4c63074 +A = -41b326c2b86e7ac14a2050bff67bb5bf9697f02594789c4a2b3e8455df4522546278d0620f28a680f6a88ab545de5829305485422f4e70a5ebf0ad15508dfe3f16ac556436d8fe8a8cde83ead549d88e0bb24dee52ebbb49159ae71589d918d3fac8011cfc3afad613ea09173856b7b79b55a2e43e0f7cd21eb9122d5f6a1fc5408414f5aafcff863b870c67b740256d317a0c58af9a81d8025a086a1f3d79f7408d4bfa06b9dc +B = 4730f03c389f9bdd92fd864177e06140c9dcc02d01fe7d37b51d44de140696f116d11bb67adf7db797edeb7c304386a7f5e37bfac46a5462a6d4c49b1bc034c2e0dfa56f14bbd2a4bfaf86bbad4f6d0dfa13c782fe680847d4b43373d7137f5c2ebe4ad58c695a7d4c407bfd888ce04abaaec60a3fd33db10eaba6b6acf0e16cb61d1beb9212c2b07921bfb5595ef1eb389200b356eafe8b5288d8f0e2cf252b38301de65190d56bfadf57f +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 23f9850dccd2af799f18268c3a2918a69019513c55268faf2477c50677fce277d8ce58a0cc06dfe389170faf5f0ae13ffc4954c746eebae66efc14eaef2c2ac9001f3c7ef7e32fdc31dd725b6a8093e33daa6d19808908e0c2d3e7c1c58e0fe9ed92f4d7cf3cc222393ca4f95feab5d34fe29116410a1882dff7cd92acb87590 +A = -10a75953e5fb9903411869a2949f8f04144d6e2d61f95704ff55a02f40c4f283add405353a68bf7d6acc1b8cce738f0c6f9271a538b4c688dbeface58eef0a0a1d491a9e66958750db97bd01466edfd245cef03bb6a3acb81acc63c38538e7f15deefd15afc422a8641c357c31a069258dc0ebb63f06094ed8fe7d4d420246b40302361967c81f0a9ca542fd1de01967514ff2565de7ae3b4a200d63feaa22fb99a251cad66624df4 +B = -351242b6e6d0122f7120deb8357c3bcf25d221a15f83579883bfb4dc2e6099e6b7b95fd08f6e573d93354b0676f7bc9fad563d6eb0f3567ef43efe3d874b9c7733e4fe1ef491043e1f80aab6094cc9b9c236570972233ea74e8779a6eecda23a65d08d878850cab6005159265893dc0f66920a12c26dfb421ec326a1ac09e9ab8085825c31aba488af02cd51f96b205c50e692dbf2d844ff0a989c3ba9f1c2bc7f2e7dd9458a72d310eb28d490 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 69c7fa326630d7de69249807cd8bc55c9315acac26fed3caa3c8a9c6b51ee96a7dd0b3bacd5cc13c15f199e268c5eb91d1ec36c085f83b437b9906caa6e39ed7bf09778610b621426cc8d36d96f541d0bfcc7693525d33e0c2ecd77ccfe80289a11155b37c7ea7791b5c2be3f9b954e230c19d746575afe9a1a3a9677d23c5bb +A = 7cb78ca8e5d903096630744c85975719c16333e2e44931956d8c45b001d35ed4e184dec88c9e2167d2f338fe6f25540a144cc419590a4ac7caedea3bbbc565365d3357baa62fdccef2c5ea616614e0bff60e81916eb4abde0c9725b1bf6869e8b1e11f6d0d08fd712bc68003e55ed462ad4946f7f982e663f65d45c07c659d9620d5139d2b3332a68d33aec36e21716a3b75f44272a19f860e6ab3864f06def9a5ddeed340ac0733353 +B = 16d5b074e008fdd30e73ea95cb5fb87de806319388b3a44f33c94d38be0e6f1a92103dbdfb3d23b6e1d19bdb29ac14833003e9482cb7524d0d7b4c377f4911e3372f2cea6f84c938d84e3994e80f0d68e7e385ca29e02f70294c921dce7cd3829c5854ce51d1f4fcf7dba910b51b48a3f53cb1f187182435f21f6981cf8440f9c8287a9749c92c0304cc2bc91eef32d8e6526be802de8aa16684e8854cb0b67d9f7ea00f6f0145d14e3c251f70881 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 72192604b2f3f68b9ed3e261120ea52b06a05869f6abd21828ce8abadb3a71c360a14947bc738e5d1d530b9636d796f785bb44508477eefa80c4b77d4e8e35463e15ea2a48c682d3288c5abeb66181e4bed7d5b4e0db20fdf5ed68513aa5ae7e0978ec1c4646368f206636ec90e808817bd1d03acf9adb9ba57dc153873fec11 +A = 1112d291463b28ef45e879412e6607a3e20d50dba5044e71883bb3cdfe9bc694a577fd7d896dfb836a171f3a4d8fd025d3a979b43e41baafaf7b535d9050e47f4880828640e952435648960bbb74a3c25dd90bccb3fedd254dfc0f031d0e8a468e93bb69f771ed35f1653cffea1a763491fdf6efa21aefc287cb611f5ea0085f64cc3705c784f87ce00846901833d01a3c45ce047d822ba390b538f0a24720155409f60ca0d90e13991aa1 +B = -d553fa2dff0265cd9d083ad097af87a99af3d8d93a9f4c07440a28a427082004ae5c81d22bda1dd2429f540de8df175c1b4d0d50f0227489ba570b28baa35055df951d05b584ae6b051a135d7eb2a501b2441f82c135a8ec0eb81d379b96ef8f2fd526ee62293bcb934c76ef8083727a4b28bbfc9f515ebcc2bb7ed9594a106e137ce94e9105b2e2f4776aa9c6abdf426a181181fece3251c3ef4f8eecb634e6bd47c5878663fd51c74a66b92713fb7 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 459e19faf105ab17ff794927aff86196b3cc3461e69cada53ab8c8c81e2b1820408421ea1af6ae10257e8cd9dc16386906410761fed62cf9ddcf0da2a92800d99563fbb9cb1ab0ba46a17cb9dee3f2b68992c2b832a5932e4533fbd5c4487d870f3fb5d7a1c358f4aef02993360915a9e9cfde234df5f51c761d84568400b618 +A = -7a964c62e38e4124cd2bad727138dd12a086a2bf01c095b078ce2f81288d3c8435ccce0c8e00229184091130989434bcd107a3a0787a2f5f4b0e8c23b1cee9a8f39ea279fb6081efb6c3df1704fae9e87d63ac6eac4c6687b3551ab7ddac5ca0541e12047d04c2fc760fda0916cd2b585a90d25880fcc1bde8f0a1a413969938d42e8b3b5f73118798e85b901c2e15860e29e2ee8b1c95336b97dc10a21f5300e0352adb60b40a8a99333380 +B = 743ff4d91ea3e0f9c4f72e5daecb4fb00b15b86e30bacebbe4384324523d14e22abe29b00573733f594d652a88d98c987f8db08b27b4dc68577784fde02dd410ebdbfaad9e9afc6a22a8cbb13a780222bd212fc61e38faf409e940fba35ed909e6938e83b0fdf5b5e3ce138604823e788efc3aa0df924554fb70fd2faf8249e17a827c5d85942005b328bed97e5ea1f1810219d77f2fe121ce66518e37c84d64aebda3c397684212384deebd520a776b95 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 183950095d9424b0ed09985aafbbd2e5d64bf541a56b68b42ea8cf9b2c051615ee7bb6c0687ca6fb0036888fbc927cb7aeb303750871442ff2c0087a95f4efad568f48b03bd2b9a9ac26af8c259a3fa97cd2af7e3d8f36148c26785489cda6c00a21e7eca219d1f41b2e82ba8e2c1cd752eb08a2fd50c6f9077f3096e2eba05e +A = -1d2fc778cf44c6992d1f3a056860eeb12f969358cadb087dcaebf5f96bec42bc0aa98672260adf1732da057e9e0d22081e33f5fa71f248cf89dd361036ad58692637cdfff584a191279f178242ec0ad397efc52e99462f496caa0f3133c4238aaa877fa7094662f080eb284c4cbeb992a368c2d157ac5c8c9160c167716406190fa39ce0abcdac52c8020969b87a4f84bc09a51f7b2ca288c93b1aac64e19623a7d9e69976a31074f637e4c82aa +B = -2f188f1245b75cd21d052ec76edeb5881944a143fee31c67370fab0420a748f3f1957bb8332ffefdeabd0ca806169629f130c86c99bab490a9668fd8200f4a9b1704c589e75b5c8c855f133d50b2ce06191875e2872b36c78438d6032d53004c047f49e4cb81e19fa84da16d053e6cbc7c8eec0b9129a8831eba690e0542ca3fefd204258624e92844c8b7bcdccab986475a47c8b22e89079ea6580ef8f496099cc24dc2911dcb1921d1451e2163b55bbb7db +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a02c38d5df9ff7055ff84122342ccdf6ed7f7d54fe8227af091371f5ae62844645586adaae99c11f4ccd828103a81471bac72dc20625962e41d603e760591bb3569a21f45bf062b86b5fd1c617a4769a4d767a0ee14d104084c12ae875316a8f2be7adec0104381dc02c20b5851efdf7d4bef0d68076975e0ada3e58e101e8b4 +A = 5daf37d616da184acb278a75fda4e4fa49e544eadcf373c054b203a309ba198233f2285a1b55dc92e05d0213b26c82e261d8383a845813077b2e1b5f4553400f09410987c8dd21d4383e0f05747d0482d1a89f160a5220b22c78393873564fc5b1e4d5627ef3d4a05612709f301381df35606e99560fba07a917d7ea7413110fb5a8290e114d5200cfecb00b6c53b2ee29911bcb2fb2930eadba0ab9dfaf46443370307d9c3b61a329f0b8b8cbe7d +B = 1d9539fdb1afabeb9be6e774dc7c7cc4bb4fd63af7abb557a5fc80a3fd23a4600de3c7fae89b91f3d441b61d3e24b2fd3d7803cd71620e7313917b4afb89ef5171a3d8a68c3c74aa3dfc8058d555eac429dfb6db40a9e0c25aacd2050418d6f32bf21cbb76981269dcd5883178d4b69a931a0338b93022a2ed0f78f3d8877989cc406f19d6d082ea344309318c56be7946412ea0867c78418ec32b9fa3a61017c10939c9345021133116933a3d1eb86a3ef16424 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 5fca287abf1f487e0ec18c230860eed4a2e550228b1500b1e33bcd6675646b5afe505b55073129f22352dc2b113c584ea1b98808214b6916933e90e036b129b61657cdea9026e1fa087ee300e055ae8f94ffca933a2d70453ed220468a5a3cf1a65d81eca11cf570d7d038722397f487af60531f24a5f069671354882c8bd2c1 +A = 1d9fe15171dce97475f4ad329fc8fb5469fb2b8086e4b01eddb6ceffe5324cfbd28d791705848569739b6758ca7e7d7d49adf0c11d891b0a5879ca870d1ca5ff475513322ff218cd26024f97623bb8a53084594e1fd64154e1db702522883fcf4c0d677a7fe90096fc76dc3800816996308d8f0be2dbf3b879f8a000c0ac534511437e2ce2d7ebcf42fd1698a829eb846b3afa581c24d5bf97abc6e247f110f4e872a2474e3acca6c8c0d518104c3375 +B = -dc0da8f7adb8e9f7b0e3f293cf623528dc8e9668317910417e52301c50c62e7d30e77ec7e38d6817d1f5a93e851f8560f642f23a0b9f836812d27b1b41c0867088a3108332b8711047560052ea30c8840f03a25c65b227a175d8f340095823788adb5bdf2b7ebb801e20f6b6435e154f78d17b8fc4373aecee56ec7b8f5686a7d22c8571797fde85cec884d45ddc4b1f2cc47ebf56a879bf286f349a0edfb531168b733d43de3b86b49eacb10b06a432c96c63440b +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 6222c1a14c6390d73944cead58eae5e7a6c19d19e4563c36cf624f5b61d99991bed7dbf6a0723abc56469eedfb1f7982987c2c7af6191178cf0933ed5f191b8117c9d726cdfa8b82a2fb25ca5436023f5860aff5fd482c611f134569ae87395dd99e5e9d400b5ab1e3064210ded096411654518110ea45899f4be2516e35a229 +A = -7f6766be6c6ca9bd1fd7ea1f80bfe68693f7ee4b5ba2946846839060d6028eabbb9079a165c1a07eb6a01239f3f14095225b8617753a1cc3d9c1e69b516d8705cfda396f4f0d05b0944a0f08b478d261e968c06918914ba87c8e7b7adef5cc2a875917d00585571542af219bd726e502b7f3f0bdf0cb1dfc6796be2e22e8ffb5b8bfac7e15e991022974e75d3a5eba214ab8a1aab2fcfcdbc6ded2abf834d1899d2e3ff94bad9c696aece045212531773f +B = 49c6f869745983cae44d33cb7ba141234905441ca53172abd1a2dd8bfeeac4b236605cd2dc5b04ff9aa13de84872145b935b85479136065d2d57fd15fbd97480c25c6354636c17ffbca33c9319d65e82523e39fab49321380a130fc160857a451a69b1d0509d5718a9cff8b49c2d677c1f66bf77333d2511f58d3eb2fb47b3c162cc9be8b012d8df70278f0e21123a69724a1f126369a236d54da026ebe222c513f24b577707b5ab4b90ab0e22b4e38ceb4181d4ca101 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 9e9cc8c5342dc6d6daf55fc9aa9f79ec18592e8b9724a66881c379245c91f06a7df50a6ba0964603a6dac97e77a55d06efff17c93d5faf107fe65788d0f56483915f6ea0f1ccbda7656eb58fc032b5771600beafdc12c2076110a9b9670bd0754ff6a72c5d6e1a9e4e42c688e1cc96d7aecd815bdf5dcb16fcd1be1275ce7282 +A = -11635fe16dafce21efb1c599305e9a16eb5651187cbf054cd9d911c13e8eafbb738013e212f9c2b3662ea15ac9bd82b5751d43a38e4475d2310945a812262309094ae9cf59e0e9f3d02c92d8ab01f5733a20f051054a240bcbe3a7b6bb3f7c434229f631c4af239d33bd3ce30a372a480fdb49b2716091d26071aef372b8bd8ee8eb7f2965a372a836000b3737d2a833a39230e721e4844e16031ad69cd45ced60a64510c1248fd776611934d8d2a913d965e +B = -3bb2cde9d3fda96fd7e6b24645f8e00b43affb223f2b5c3f4b7cfee905ddd6703a9d6c01f1f099ad1174da215a645ca4707d8156e762e2a253d7cfddd05ca19823ada9d33924013f677cfe4d86bde025391e0aaf91c6b776a9cf8a09dcad7cea59ee7aea1cf5f5bfe67c9d4456332d1f98e5310db9a0230381e1867a8f75b8757283f911f1a5e0d4afe5d544afa8d86637f9c9d87428fdcf8b4eb8f477e617960948253b24565b2f23081c47e211cd3c788a92732a49077f +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 30dc89bad4b449d1df9ea9b8f9d40b323c71d7e1133bc44d33bdb87c38cddedf83bb849e83436e4c92a06546fcf3e24ce6cc89d2e97a48aff2c7e3703da1b167a112f662a89742355e11e131e41052f1b379753cfa32cb0efa3a07465a258c585cd68c86bc9a473f5262c86c50992aeccbb9725b69ea8b3a7ebd2b6a24db52dc +A = 60463fae1e9354559160d55a453c12d75775a53d1606d1fd16bef7e4ad1c78f9568954112f9280c46781180951534c5372dd5aaff3f33ac9c2e0ce4934d7009aad2ab5d6a5e5a141a36846e8925c7a28d116c68fb78aa9a687ec9bef173c1b69e0d7261f96eacacf237e1fe5874e5d553985b0fe7692ce8f2a5feab9ad9a2ad9c4bbf050b73b8030ebc36b94af8c6ecb67f8c94607d80cf600efd4ce4aa006f9b1832da8a1fdf8a564be0b4369149e8639e1714 +B = 15bfc50290b771ad147695a4c6701c47f2e8aec0657a4ef999eb45685200981b0ab5f8abc143d64878b85e9548651a1afd0913e3b14d11d3a26ab9793596801662a67b0062fdc8888feb029266f71d170518b6a4a040f59996bd4f257f221e830d0faaa9688aaa6afbc1f9b40d25097eab9d71d80aabc085f3a07e48bcfb37119aa00de60be55fd07d5b1281adf7b98bb589cdf2026252edf2f075ee176e23afa6b1f924c9fcf3c34c76752e833278a2e6b62017b88b77eece5 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 8b506c9bfb75ab7ab420ae6c9b371ef035fab512188d9df76f0b31831573b44cb08266186a04d20cc761d61b6df3e33ecb86c269205c2c79ae6aa4d3ebacac8ec71d9bce1d7ab146530b131c9038041c6ce8152a6f1c09b9bec8eea4462dda0f08d75edf296eacbcefd62a0c197ed30f799343268bf6edfee4995958db7e0420 +A = 11c16713fbf8bc9696782cb5a88174cddbe68a04e8fe93dd074aab33dcd85f92baa178b2f3b8817be0cecb802cfd3ebb06734c9d399a1f090e3a8a2110aebbba0e920427bcda74bf11700b945985bd532286d44a1a615cf7c501412e454edd647f8371cb8149474557a0d47cbb782f460de7a3cc28991491ea0fc510286711b882987b09341c079565414f2c930e7c3c3a3e3e0f1d786260a7f45c70e0fa20dfc63849906af61707cfdf5a9b7a4291a1c1586d16b8 +B = -cf5638af39c6da3757a09a92e0bd54f852742682dc91c71dcdc6e72f7825a0979a1ead2e158479ce5565d22472dc3853e6bf7ba43296a5e0e0a355f0703cecc02ec79da83e3e9de10a6eccb858dedf7d4c400c27486a5b8cb34d787cde6a5fd271e83a6cf66057838fe30db1f30663cdfc22ef5d002b0b5a05831228ea200f95382a58d0d8aba36523d9b5cb7506f193131916f3ab66ac9552c26cd0c2ab1c449eaeb8fde752f4f3c3f9b060cc1f8a1e37c4fe5ec306674b66158 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 347706abeb168205cef9b0b8c6b9d6449ac501af7dfbdfbd41a20a6a47872cbd7d4cd32f7b0805ecf1573d534418b7cce98181e079d5061b02639fdf0161cea5314dbbb2ef39ec841f695281f3c7de45f33664e0dd1658f645adc1dd225f781a3fb1634517c556403587b2aecd56dceca9ec19b930cead2b1d303aa056d28bc7 +A = -5e1c869e5dbcc684c245d5c69093bfeaadf388cbf928d33a8ae2148a2b5145937e4f654c5f6a36de1124bad1de8bcc9067fe1f9a44fc6ffe55ce7ed5cd0dbb6337b0e1e96bac1eb2a3606dd97b0bdb975ea59448be50191cc7ea36481ca9fc85c1c3e1c97378dbcd6b355622046888df2ab3d18d805f4d31d464f62a8e630e955beeeb5e00c70242b8f8df708705abbeb95dea3561756298b5f3f7fe16e965294eeeea4546f5e8bacf9d6b4f2136d2e206a87dad1f47 +B = 70225f0cadd328be36ece2172c836405db3fe80ef99ec74fca25406b73a537adf5073f2b550abfc4c0fcc2c2850dace0da9a266768cb4d5ff7fc6c1c248ad74f47592101b61ef96c1302924381abbd96cf49f50c44bf7e0551721a8ae85abdf9925548d13b8c5d1a27be8a40d0f43eec3136bc3035057b75aea779b4262cc66e6bc68da93c218f1920979291105d4b02117d66deb92c3e511aa588b27130202acc9f69521957f79c7e731bbd5461552b9b6b24240dd71ac449be9777 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = a2cb238f326d47f95869e2dcb295eba819a443dcc7c2785461389b58327742702f4c86e47af129f1fd4611cda93631f9333c358a29121d58286333083d13e66f30a9533b77ba3e26089e7eff7baf19bef8054af4e24735525908864ea9c4756b42a69c897003cab7b63cfd9a5927ed562e29845308eb2a55e7f8f03c87a5b7ce +A = -1aa7ae6f56c38b654b281525b9da953ef366c2b9cffd3042105ed428dc7e5f2f2d53ef90b468bb471753606cc7a3775d86bcd2f4d5119cdde3c487cd39bf31752c5ba297e529c1b8121487e0e1de702156d0166ccaf51888a24fe7b48624eefaec855e2200929c21858676ec9bf4ceed0a832b69efd5065af544e49a3d209b85a77b0953652cbf0aa897527c52c9a98de9ae4c827f762e251478c88d410123625ea52b3478b52f6b9987d42009ae427763357ab53195772 +B = -226630b6fcdb5e274a25066ae2ca2c803549dbb935a97c0d7f6ab2c971d74cf6acd265c9d6815a6b2dd23dcb3c23b390fe8b1bed92b8c64c76c0ce62d5e7ddd7ce445bab0ca905dcfd0f128e5f4ffe966f3903d7ff1c61fe174e373cfe35a6d83249ec40b4a354d46fa1c90682efe468e895ea3da710838c262e8a47752dc6e7a79fe20051f51180173b58e0aa37b22eb8efee5b6dc264459ce4d135f430cb15afbf8c53f0de894bd2aca1f7ea32b4209a22a075f7b3b18e86f778a9e47 +M = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48 + +ModMul = 9ea62ef634 +A = 55cc58c9d8 +B = 6b49179821 +M = f753311ac9 + +ModMul = e9ab3a2aa60edd30108 +A = 5134a36c2bad180dd5bf +B = 2ba6485656d041690666 +M = 9b9cc4409e86c8b0fbbf + +ModMul = 621f9b797e866028b7bd1ff828bf29 +A = a202338dffe171c99434d84f3 +B = fb71eee7045b3e3ab5dd809dd +M = b3e6e8d53b7249df670e3c59c55d33 + +ModMul = 808d463d06b7b7f98e3cb2783e2196c349d62672 +A = c669426a92d3cb5b316e2b5b9 +B = ccaea3874008dcc92450d8b2f +M = b04dd2bb325baed1940cd000e8cb2d786009ccd5 + +ModMul = 872164b92b9426b237858c4cdafe1694f96b0e0e4c19e894a0 +A = c3255cb24a813e27c3dc410f0 +B = b144f39e7c2d33605ba7bee16 +M = f3639f4dfb782f3107eb402fabb5fc878903acb5e02e129077 + +ModMul = 6124d7d171 +A = 235b938139 +B = 3a56a22a28 +M = 83eb4af4e5 + +ModMul = 9c006f56095d442ba98c +A = 207e14237c42e3764e5e +B = 8a495a26872432fa8e33 +M = d0cf2b8ae5c67d6736b9 + +ModMul = 97387cfaef652932a230c82de59cac +A = 82ae0fc5e943af5bb8c4adebb +B = db1279be12d59ba3a9c036a61 +M = aa36dc1d13390169cd54d711eb511b + +ModMul = 32ee73c98da657464c6fed4274df20b099689e00 +A = 9baf08248ee24bcb17714e420 +B = a7f0428147bfe098666180749 +M = ce0bc198331c9ed1d21f0d498326e8185d3d602d + +ModMul = a8b3fc0b53df3b92753edecd6fbcc5f4840dad3a44da704e34 +A = b36249e259b303e453757721c +B = f0c1db50670d92abd93bdc84b +M = b05cf978bf2dc7e093d7d164e46d547219c480382df32b33d9 + +ModMul = 2663b741ff +A = 58c8e7f7f6 +B = c84681fc87 +M = e0a50dcb45 + +ModMul = 21af3c0b42328f41b81e +A = 1f79f5b5bf78c9700d +B = 5bd1734ba0f0e59c2a25 +M = 9ff3fdfb5c089244f327 + +ModMul = cbc280b5106c2c36cb31ad7e7c986c +A = cadf6482b769e83ce7f7277dd +B = f9862a06da1a9c89547b76c61 +M = cc36144c88139ce921d2fd1740bc4b + +ModMul = 3813f2fabe016e19fd8e70687ff473651a5fbb4b +A = 9c51a5bacb5d9f055a9ac2962 +B = bfed5625b21b4e82d1f105a0b +M = a47977acad7c5deeb683ccd265cb30cb193f22a9 + +ModMul = 76ff291a02715fc87ebfb3e99153c04e53358dbd7beae43478 +A = 997c4a7b537d9500d73a205a4 +B = c679ce666af284a459ae5a26e +M = d0d0fd4922953941acad8beb65c00603b19eb44fb8ca51e3c9 + +ModMul = 1a90c92fdb +A = 94fa7bb475 +B = 564b0a3339 +M = a1501bdc75 + +ModMul = 5e7ae5470686bad7996a +A = c725797912c6c5f30d94 +B = 3a7f4c99ee3f5fa9582c +M = cc50c8b7408f09a74973 + +ModMul = 72a15b13bcd1b63747342a6be8f0f2 +A = c33357af48a2df569e3c11ce6 +B = a4b4c5c14d7796adab54b6cae +M = e22a0fdca62a37f4c8a61c96a429b9 + +ModMul = 31e179bfbf65b0695dde36a4fb72d131830dcdd6 +A = ce8d3adab8cbf15c332c0b289 +B = 9333f94eeb7d7a86b82becc51 +M = a532a76bd5cff409b580d54d12ef75ad8179b381 + +ModMul = 8f4b8a585415adff3a7bc35fa88891ba31e4a82672c664fb14 +A = 9a2b56a54bd0727ab4be57ff2 +B = edf1781b4296567990773005a +M = c5a7c3b97ba00d6f174a019c6d37eda52036c528f351bef0f1 + +ModMul = 917bcdb402 +A = 55c7dbd314 +B = 997b29ef79 +M = af5b4cbd0f + +ModMul = 660c4bb2b771f523a4fd +A = 43fe52461d5139620a11 +B = 1f8ec4b67de1db54ddda +M = d0458e215b7e6903d96f + +ModMul = 7aeff02c143e4426fcbcf32bd1277b +A = a2671586369a990dde7829f36 +B = c7ff67937c900daccc0ab1d8c +M = 8ad9c1d4d3cce681d1ae27c27982df + +ModMul = 4b153d57433f0f7276674d3484e9bd0d25227d07 +A = aea36cf51dd2ce06c66b7a407 +B = 80c9fe5bb0afd2bf8b3644f96 +M = 8cc22a67ed7e5a7a2322aaa09ec2be94998494f9 + +ModMul = 7f8447dd983b113f04c6288f9539e53a2e9cddbca8b2fefcc0 +A = f67636b03821c8f13f21217a5 +B = 8473a29f4ae33f36a0d2c6dc0 +M = b829af37b557c3ddbb5257c8b19144b90708a45a274d6655f5 + +ModMul = 17fe4644a2 +A = 912611576f +B = 7a10d36b80 +M = c5fa605133 + +ModMul = 8159b23d4fd697b4fd35 +A = be2d646e76494439e60 +B = 60fa770d05ebc69772b2 +M = a6e7c940cd749925a85b + +ModMul = 7c412dad5c9fff91357bf181caf2bf +A = 80f476ed5acae75b34ed54c52 +B = fb818e2bdab3b5f4bd84db3d0 +M = d0339f7ee41337d8462d1a9c207d1d + +ModMul = 70432c749da4ade2c38237545ebfe6c4c6a92f6b +A = ee9c92de52210e61adaa6eb4a +B = 8ab55a85b1abab62d33e75fe3 +M = cd3faa6de4cb62fece4c3f94492d457834a6a041 + +ModMul = 9fef1c18778a8691c5e71c0b5208e82778e9bfb632da0b7e28 +A = bd162c90bed25e84dd5b6b77c +B = d887ee03020c5df356f091db6 +M = a2c2d45fe9decd93a0ca3edab8fee46d27ba23fad9b5294d5f + +ModMul = 958951bd0f +A = 12bd0d3375 +B = 668bb65b4e +M = 9c617dfaad + +ModMul = 8a109ebc9cbf86613e43 +A = a3e7019f1bbc35689a77 +B = 3189ecd3fd4ffd0229ef +M = ddadc50600dff2abc1af + +ModMul = 2b4d9f85a398c852b3a0cc82524619 +A = c244fd157267f707319ba6c6d +B = 8a07018a748992429bbdbf326 +M = bf3813fb54f749ea5627f59ce30e07 + +ModMul = 28cab7d574e6dc56a6a622f8a7523cbb8dcc5e0f +A = c9909dcfd3a59a3cfa538b267 +B = 8bbf89cd5a4e24adc2d8c646b +M = c8f02682b9d480ea98faaca53b747ced33ed0419 + +ModMul = 69b2dfb3f1d8dbb13e9e479f38edcc427d5968acb7751a226a +A = 8019266c548982a520ab48eff +B = d33c3e3b13576dcdb3ffaa796 +M = e6255103732475604df7c6f7ef7e6b49a8ef9e2b0c717925a1 + +ModMul = 3eaa4c99fd +A = 6fc42faa85 +B = dd0b4e318e +M = fd7f22301b + +ModMul = 56b6b811ced3433755cb +A = 145573d17cb0c996c69 +B = 9d3297d5ccc184896822 +M = dcfb3b383506239e83e1 + +ModMul = 34315b6bc6d3690c28060485ae331f +A = b963a26973894cfb42fcb2d22 +B = e8523304bbcdff1a0ed4141bb +M = d7a379aeac7d8cf94f19e7924d35d1 + +ModMul = 2ec9466e8b3357496f07e37ba24d36a237883846 +A = a75f3904e564997695b6707eb +B = f9f47bd779834dc1f5fba0654 +M = b3ae5abed45d09c4dc5abcadc3ac9abebe1949ed + +ModMul = 88b4d86b2c1e1bd780e8d2499c2221e05fab4f9b7047c2a044 +A = a38eceb9c551f0e69a544072c +B = d5f8e7c2d534b2b8985bfd213 +M = ff81809b84fb8eed3508ad891d3d8208249d8a902a12d6acf7 + +ModMul = 172f2e2e22 +A = 1584ff1055 +B = 2e0aee014d +M = b904cb0bc9 + +ModMul = 122c10d3200270b9eaa1 +A = 86fd189e62a6dc1e4ba0 +B = 5235635f7b0336f5f235 +M = c93da97d0e95fb63dc4d + +ModMul = 3e461e10ac4eb749512097fbf76616 +A = cf4ce10cbca07164f3812f89c +B = b7e4639c233fbb0f923fb5104 +M = 949647857e1406871593fad5c30101 + +ModMul = 88117b59d9fed79dd6aaf083ee938215a995a221 +A = 94c888795567d434123d441a7 +B = c60ca79e61a352e34e0f78bee +M = d2553a7c5dccd639a3927697a2e1af03845f2f25 + +ModMul = bc5f0076a8c2f6cc8f4e61540d2d6f6d6b13b775b363dcd71c +A = c170eaddca5295d6ec6272dc2 +B = f94a5685ced7661df2efbd34e +M = fa6bc46aa05033af72aa42793e9174af2e3ba38992f33572fd + +ModMul = 1110cdbe5b +A = 5db02b38f3 +B = 3369537903 +M = a8863f7979 + +ModMul = 90fcc5f3a346d3d4ea4c +A = b93373680ea0feeb31d8 +B = 37f9dfaf0e180be64bd5 +M = d595cc29237d1c19e2db + +ModMul = 8623a9997e514cf3c1d06c33c14053 +A = b396f5ede6212f1fdfc7e7b77 +B = 81a1ddc18306f2d2e84030148 +M = a6be32a91b34857842255ef8b1aafd + +ModMul = 63f8f0254df06356f5cab8941b77619ad58025ed +A = 806b2627b08d987438f920bae +B = 83297039f4aa8efc1a185fea3 +M = bb8a7e7c19be02c25cf5682a0eee655fcd5b69a5 + +ModMul = 697238dbe3d395e81f20c9fcc8db30c234a1f75f3b2bc27438 +A = 930b04224bc097ac1d8bae8be +B = b79496a80e45212c4663e5b64 +M = 8ff7e19d967d317c255380411898d73e3786269f09079f19f1 + +ModMul = cd93b5b8b1 +A = 47a51b2d5a +B = 86d6ba5155 +M = efb0ad3643 + +ModMul = 2037821ea789118bde0a +A = a92215dcae19be637ff +B = 93b9a3664a406737958f +M = 9df360b69ed26f610253 + +ModMul = 3bf11785d28ceb668dc55b870faf7b +A = bc8758854dc48e057cb6210de +B = f03ca689620a77ecd8a6f0de3 +M = f3ff0747d6e5f34a0ba4200f579259 + +ModMul = 7b30b44f75ed12f54136858ce4fe77d00e0952cf +A = 993cd09f3e46423a8ba2053df +B = feabee384158032dd013dc08d +M = cd0b21388cb2033b1e792ec4078334df70b6c8f9 + +ModMul = 8ce1e17972f1a9d2e9437d0c5219354728a5f31337808d7650 +A = 90e5d18b017118177ffb080da +B = f8e7e09032574f6c66e623ec8 +M = da795e6ef63ff7dc4baef5c327022ccf65d44e3c4e24823f11 + +ModMul = 8fcd412054 +A = 2e7f9b1a +B = 6283de2c9a +M = 9bff560ae7 + +ModMul = 57d0d3b79f1e2f3632fc +A = 2f8cc403de5af54cfa39 +B = 3b798c3ead52878dfb2f +M = 805e6cbde400d4b4bc9b + +ModMul = 23331614e88633af879201f568c359 +A = f21f19da4b20980979a645dac +B = ea752050b79883dcd69222536 +M = aed3faf4c88f7c4afe257c5ed90599 + +ModMul = 56dcf9ae1c787e773774df3c8762babb4675a212 +A = 9accf901fa599da05fa6ab5ff +B = f7f6b9b1d7bae06237532e39f +M = b5bcd776bb2eb0805ade3c8b47e883962d3cbdf5 + +ModMul = 61d0ee0786963906a028a1df01f836841ab6d39d88ca2717c0 +A = 8e57680f213d088ff1a1e7db3 +B = afebecc9943b0093f87022940 +M = b6201f68a45265d7e9183c3255feb4c110c05dadbcb13881bb + +ModMul = 143ae78a29 +A = 334abb952a +B = 74203e7a50 +M = c9535a9505 + +ModMul = 897a2b57e69f5a1469ea +A = 1ec8ca0ea4fed52bdbbf +B = 3a6273cab05e478a57b8 +M = dcb33163a8ea42c1ae6d + +ModMul = 4a2c10e90e2d37111db79a44d3e31b +A = a90e7bbd63fc4af6de83029ee +B = cf09c3dd50b41afc7045e057b +M = 8ab85d47e4270116a64f97dc4f0f15 + +ModMul = 70f94276c9d85fd3f71edfaad6051456f754da85 +A = fa3e9ff6e1aa1fb78e51711cb +B = b115ed197c50b7ec4040ca255 +M = ad63f69ef1346e7549ba71c13b24b279f53bc9bd + +ModMul = 861e7ef401866f815b983ba18a612913ecc20a67016d79cfac +A = fc41a9ce06e882942f751be7a +B = 881c05a51d1ba8134d126a48e +M = b12200b39526c33b70e8aa23ebc400dea0d4d8fe42be103d5f + +ModMul = 4e0051898a +A = 2a06523f70 +B = 651b5044f0 +M = 9da4eb09b5 + +ModMul = cc8274c88d6affc3742f +A = 9ccf0133f9628532f4f6 +B = c1d80907057be7a67b01 +M = d6e76e362da831f32685 + +ModMul = 568f15bed5c4405be9dd04673a9c46 +A = dd6029c3196feb6da7f0f4a48 +B = a5f6745f2cb64913d1d3236d8 +M = f62f02c9b9ca8993e3be9a02b444bf + +ModMul = a629452d5ed19df040eca26eaca37d82c0fb1d8f +A = 963c51a9415b03e85ccb09f25 +B = b1cffe333afe44311cb968ffe +M = ab2128698d498e8d75455033cfbbf4487535773f + +ModMul = 814030123025d287aaa8b826792999d72f2d589e0c7f7f3dbf +A = c3b33f391e78bee97ceddf313 +B = a9136f3af450fdeb245eff425 +M = b6aa9c517eaecb70781e597b907583bbb569e970d229235a35 + +ModMul = 8735bd486d +A = 563e15c52a +B = 31293264e1 +M = 92f4b193df + +ModMul = a541f69ca163b288dd0e +A = a608b48c1dcaa18424b2 +B = 891b0b296e911068b00c +M = d4140921f4b2c84f1eb1 + +ModMul = adc1b7cf65967b013d046866b4ed9d +A = e97941448f65060cf63ecd486 +B = ca68936f76cb87a8fbdd37311 +M = ebbca2482fb82eeca2866057cf1179 + +ModMul = 44aa9f0dd58d4510a7364e130698b34eda23a632 +A = c11f83f01bb964ffac93a2e30 +B = e05ee40eea39f4538d735193d +M = b5e8b511738979dc740a6a1f7291cf4561787be7 + +ModMul = 8b16b82f064f471983c7154abc9f9ba355111bacb90400372a +A = acff8da571e1c96810bf95707 +B = cdd23e5504cc26d0c34a62b06 +M = f38902a99190ae0b5ef26849a6e943d651925666fea271fee7 + +ModMul = 193f453197 +A = 8cb3078675 +B = a8fb003a87 +M = b60ff22f4b + +ModMul = 849c26c8cf5cae426a80 +A = 5d1e3d2b4d038a0a34be +B = 34f70325565bf0523314 +M = cbc189f9a732cad8f425 + +ModMul = 9a4e64ff530c53a4c6c5b6b5021920 +A = f53b81723cf74f520a61e614e +B = 9d8ac2e6b839143fdd079a2ff +M = a115375435151798f3644bede9d863 + +ModMul = aac303a4623e80158af1cb3331965cc8e3184edd +A = cce0a88606ff962fdc37e72c9 +B = 9840a500a2051625c517104db +M = b99dafdbd91ec3c05791031df5e193c03d6a441d + +ModMul = a31401dfa761bbe82b66b5f094151865b18a4ba75bb9b3dedf +A = e6f48c027284856aaf3b96425 +B = b4c326f72a6a22fd4b93ba5b3 +M = e57d9608ac6e5b129b2c014958bfc59137f63838b1ba88a4ab + +ModMul = 8b0929adbf +A = 61fdf77ac0 +B = 8892f05400 +M = f12b3766eb + +ModMul = 91b57f353307b173679d +A = 33f8e73752072b4b5cfa +B = b4c730f79f4f2c07945d +M = d41be1d8d2e5753e3ae9 + +ModMul = af04c564adfeb120bc4770bc8c650c +A = af151333b3d4cd1d29fd801db +B = 9ccaac44ff91be11b30bdcdd0 +M = e0bd6e70d5f5ce08fbbfd48d43101f + +ModMul = 1b8d623796a5065d9e993a53a9587a0fdbea1bbd +A = a2fd08df2d4eab0cd6d29e213 +B = 92c9d26ae7c215b52199ee28b +M = cd529f4cfa46f3bd3e7fadf167fdc02f6f881da3 + +ModMul = 4a8573dd8dc50a4fa39f3579d3869745eb8c1153ca508deefd +A = 855f941d085305725da617f5d +B = 8f09b7d2c36e0340523da5421 +M = fd8caa05edeaa81beefa01957eed97a981ab34bdeb6d8c704b + +ModMul = 2d278e089 +A = 59d20a1716 +B = 8e2a58bc75 +M = b3d61ef699 + +ModMul = 2f937ce359d0f6cedd1 +A = 1019d11d26040ffd5b1d +B = 7cdb6252087423d43e08 +M = e8f537323004447e669f + +ModMul = 6567332e25af83089f7458786ab0ca +A = bf9565e9f8a098894447b58fb +B = fc867626f268c24cc0ab7bf8b +M = 930f39183353363dcd822933a438ef + +ModMul = 3692e73ad1d91ddc19cad3808eba2c5fc88e2bf9 +A = d0a42ce512629f0ffd233a9aa +B = 97f6d3c4c655c7353a62d6ac4 +M = eac2ea84851f880214b8f40f881a2e56a6ba6f2d + +ModMul = 81df390c9e51b30bd639db15adb464c7cb1d011cb5e260be58 +A = c237eb242c40960861c938c08 +B = ab2f481f0d768eebd90d2574b +M = 8697d7a28a5f42c9a7b31949b8b568f861142f44fe66c6cd3f + +ModMul = c952f9aef +A = 81973bbcb3 +B = 28ddee3bf7 +M = c4a40993c9 + +ModMul = 241dd53d93f7bdbbb2ee +A = 2136eda4495c45c9f96c +B = e74c4baa8ca3f6b7cd5b +M = fff4594e7a5f0a1d3e15 + +ModMul = 5f861ed8b0aa835761613e6c869cfd +A = bfc5c1572086079f5f5d18d1b +B = 95902e14923c8010b7e905178 +M = a819c6c109d623f9b845aa23712c9b + +ModMul = 5b8ab089c4e4c6804e48a2bc1d218718b3a32598 +A = fbe65d3852224a812c432672a +B = d57a3f38da966d2471d70a048 +M = b9e6a626d3ad026d14248fc90c882bedd64a1f13 + +ModMul = 761438baf5b02dc095b7040e082da7b167c2b9ace956284ed +A = fd91701ed2151f8e994bf4ee1 +B = 88b66e735b76972bccd9db182 +M = 8008b2d1274456aa68dc627b1ec3e1762c6ed2d660c64a1a55 + +ModMul = cb743c97a1 +A = 9c69ca9b60 +B = 7488f48f5 +M = d67040ed0d + +ModMul = 931b2bee1bc30725a31 +A = 650f567b544ce02303d4 +B = 5858da30dd1fae88a675 +M = 91ce30234bb29fb9e833 + +ModMul = 5b4f262cec958a20390b5e568ccdaf +A = f7e240e8a077e8e87506db2f1 +B = f8653fe64e3bd414782f51634 +M = fdb8225eefc1620648737d31dfe1f7 + +ModMul = 4c011d1ddfa30c901793cc6ce74db47584cebbd1 +A = eda8e9a9ea3cdae17bd50b1b4 +B = 992e8ef4a45593e4ceff67876 +M = 95e2f120cfcefbada1058af6c8853cbebedd5763 + +ModMul = 6e99aa5b8107399848cf24fbd88ed6350efb68d737e505b466 +A = ca6c51ba2f410d09bf71d60fe +B = 8bdfa8fe5ef3b2ad02bc63c4d +M = 84daecf412b8c50ad6dfdb546c3eb783dcc6f32003eda914bb diff --git a/crypto/bn/check_bn_tests.go b/crypto/bn/check_bn_tests.go index 61deee274..9e710d8bb 100644 --- a/crypto/bn/check_bn_tests.go +++ b/crypto/bn/check_bn_tests.go @@ -211,6 +211,12 @@ func main() { checkResult(test, "A / B", "Quotient", q) checkResult(test, "A % B", "Remainder", r) } + case "ModMul": + if checkKeys(test, "A", "B", "M", "ModMul") { + r := new(big.Int).Mul(test.Values["A"], test.Values["B"]) + r = r.Mod(r, test.Values["M"]) + checkResult(test, "A * B (mod M)", "ModMul", r) + } default: fmt.Fprintf(os.Stderr, "Line %d: unknown test type %q.\n", test.LineNumber, test.Type) } From 28a8c2fe25abba681aae35259eb7d5181baa2252 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sat, 2 Jul 2016 20:48:45 -0400 Subject: [PATCH 34/40] Fold the rest of test_sqrt into TestSquare. BUG=31 Change-Id: Ief7bda365c3d786f946caaba0ab2af03c50459c3 Reviewed-on: https://boringssl-review.googlesource.com/8609 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 77 ++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 196fed6f2..bdd5ff001 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -105,7 +105,6 @@ static bool test_mod_exp_mont_consttime(FILE *fp, BN_CTX *ctx); static bool test_exp(FILE *fp, BN_CTX *ctx); static bool test_mod_sqrt(FILE *fp, BN_CTX *ctx); static bool test_mod_exp_mont5(FILE *fp, BN_CTX *ctx); -static bool test_sqrt(FILE *fp, BN_CTX *ctx); static bool TestBN2BinPadded(BN_CTX *ctx); static bool TestDec2BN(BN_CTX *ctx); static bool TestHex2BN(BN_CTX *ctx); @@ -200,12 +199,6 @@ int main(int argc, char *argv[]) { } flush_fp(bc_file.get()); - message(bc_file.get(), "BN_sqrt"); - if (!test_sqrt(bc_file.get(), ctx.get())) { - return 1; - } - flush_fp(bc_file.get()); - if (!TestBN2BinPadded(ctx.get()) || !TestDec2BN(ctx.get()) || !TestHex2BN(ctx.get()) || @@ -484,6 +477,31 @@ static bool TestSquare(FileTest *t, BN_CTX *ctx) { return false; } + // BN_sqrt should fail on non-squares and negative numbers. + if (!BN_is_zero(square.get())) { + ScopedBIGNUM tmp(BN_new()); + if (!tmp || !BN_copy(tmp.get(), square.get())) { + return false; + } + BN_set_negative(tmp.get(), 1); + + if (BN_sqrt(ret.get(), tmp.get(), ctx)) { + t->PrintLine("BN_sqrt succeeded on a negative number"); + return false; + } + ERR_clear_error(); + + BN_set_negative(tmp.get(), 0); + if (!BN_add(tmp.get(), tmp.get(), BN_value_one())) { + return false; + } + if (BN_sqrt(ret.get(), tmp.get(), ctx)) { + t->PrintLine("BN_sqrt succeeded on a non-square"); + return false; + } + ERR_clear_error(); + } + return true; } @@ -931,51 +949,6 @@ static bool test_mod_sqrt(FILE *fp, BN_CTX *ctx) { return true; } -static bool test_sqrt(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM n(BN_new()); - ScopedBIGNUM nn(BN_new()); - ScopedBIGNUM sqrt(BN_new()); - if (!n || !nn || !sqrt) { - return false; - } - - // Test some random squares. - for (int i = 0; i < 100; i++) { - if (!BN_rand(n.get(), 1024 /* bit length */, - -1 /* no modification of top bits */, - 0 /* don't modify bottom bit */) || - !BN_mul(nn.get(), n.get(), n.get(), ctx) || - !BN_sqrt(sqrt.get(), nn.get(), ctx)) { - ERR_print_errors_fp(stderr); - return false; - } - if (BN_cmp(n.get(), sqrt.get()) != 0) { - fprintf(stderr, "Bad result from BN_sqrt.\n"); - return false; - } - } - - // Test some non-squares. - for (int i = 0; i < 100; i++) { - if (!BN_rand(n.get(), 1024 /* bit length */, - -1 /* no modification of top bits */, - 0 /* don't modify bottom bit */) || - !BN_mul(nn.get(), n.get(), n.get(), ctx) || - !BN_add(nn.get(), nn.get(), BN_value_one())) { - ERR_print_errors_fp(stderr); - return false; - } - - if (BN_sqrt(sqrt.get(), nn.get(), ctx)) { - char *nn_str = BN_bn2dec(nn.get()); - fprintf(stderr, "BIO_sqrt didn't fail on a non-square: %s\n", nn_str); - OPENSSL_free(nn_str); - } - } - - return true; -} - static bool TestBN2BinPadded(BN_CTX *ctx) { uint8_t zeros[256], out[256], reference[128]; From 45a8c8a3c4e1132989471a80518b15230ac93de5 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sun, 3 Jul 2016 10:05:56 -0400 Subject: [PATCH 35/40] Convert test_mod_exp and test_mod_exp_consttime. Honestly, with this size of number, they're pretty bad test vectors. test_mod_exp_mont5 will be imported in the next commit which should help. This was done by taking test_mod_exp's generation, running it a few times (since otherwise the modulus is always the same). I also ran it a few times with the odd constraint removed since BN_mod_exp is supposed to support it, even if it's not actually useful. BUG=31 Change-Id: Id53953f0544123a5ea71efac534946055dd5aabc Reviewed-on: https://boringssl-review.googlesource.com/8610 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 151 +++++---------------- crypto/bn/bn_tests.txt | 261 ++++++++++++++++++++++++++++++++++++ crypto/bn/check_bn_tests.go | 5 + 3 files changed, 299 insertions(+), 118 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index bdd5ff001..f2a33ecb7 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -100,8 +100,6 @@ static const int num2 = 5; // number of tests for slow functions static int rand_neg(); -static bool test_mod_exp(FILE *fp, BN_CTX *ctx); -static bool test_mod_exp_mont_consttime(FILE *fp, BN_CTX *ctx); static bool test_exp(FILE *fp, BN_CTX *ctx); static bool test_mod_sqrt(FILE *fp, BN_CTX *ctx); static bool test_mod_exp_mont5(FILE *fp, BN_CTX *ctx); @@ -174,15 +172,8 @@ int main(int argc, char *argv[]) { "| grep -v 0 */\n"); puts_fp(bc_file.get(), "obase=16\nibase=16\n"); - message(bc_file.get(), "BN_mod_exp"); - if (!test_mod_exp(bc_file.get(), ctx.get())) { - return 1; - } - flush_fp(bc_file.get()); - message(bc_file.get(), "BN_mod_exp_mont_consttime"); - if (!test_mod_exp_mont_consttime(bc_file.get(), ctx.get()) || - !test_mod_exp_mont5(bc_file.get(), ctx.get())) { + if (!test_mod_exp_mont5(bc_file.get(), ctx.get())) { return 1; } flush_fp(bc_file.get()); @@ -635,6 +626,37 @@ static bool TestModMul(FileTest *t, BN_CTX *ctx) { return true; } +static bool TestModExp(FileTest *t, BN_CTX *ctx) { + ScopedBIGNUM a = GetBIGNUM(t, "A"); + ScopedBIGNUM e = GetBIGNUM(t, "E"); + ScopedBIGNUM m = GetBIGNUM(t, "M"); + ScopedBIGNUM mod_exp = GetBIGNUM(t, "ModExp"); + if (!a || !e || !m || !mod_exp) { + return false; + } + + ScopedBIGNUM ret(BN_new()); + if (!ret || + !BN_mod_exp(ret.get(), a.get(), e.get(), m.get(), ctx) || + !ExpectBIGNUMsEqual(t, "A ^ E (mod M)", mod_exp.get(), ret.get())) { + return false; + } + + if (BN_is_odd(m.get())) { + if (!BN_mod_exp_mont(ret.get(), a.get(), e.get(), m.get(), ctx, NULL) || + !ExpectBIGNUMsEqual(t, "A ^ E (mod M) (Montgomery)", mod_exp.get(), + ret.get()) || + !BN_mod_exp_mont_consttime(ret.get(), a.get(), e.get(), m.get(), ctx, + NULL) || + !ExpectBIGNUMsEqual(t, "A ^ E (mod M) (constant-time)", mod_exp.get(), + ret.get())) { + return false; + } + } + + return true; +} + struct Test { const char *name; bool (*func)(FileTest *t, BN_CTX *ctx); @@ -649,6 +671,7 @@ static const Test kTests[] = { {"Product", TestProduct}, {"Quotient", TestQuotient}, {"ModMul", TestModMul}, + {"ModExp", TestModExp}, }; static bool RunTest(FileTest *t, void *arg) { @@ -670,114 +693,6 @@ static int rand_neg() { return sign[(neg++) % 8]; } -static bool test_mod_exp(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM a(BN_new()); - ScopedBIGNUM b(BN_new()); - ScopedBIGNUM c(BN_new()); - ScopedBIGNUM d(BN_new()); - ScopedBIGNUM e(BN_new()); - if (!a || !b || !c || !d || !e) { - return false; - } - - if (!BN_rand(c.get(), 30, 0, 1)) { // must be odd for montgomery - return false; - } - for (int i = 0; i < num2; i++) { - if (!BN_rand(a.get(), 20 + i * 5, 0, 0) || - !BN_rand(b.get(), 2 + i, 0, 0) || - !BN_mod_exp(d.get(), a.get(), b.get(), c.get(), ctx)) { - return false; - } - - if (fp != NULL) { - BN_print_fp(fp, a.get()); - puts_fp(fp, " ^ "); - BN_print_fp(fp, b.get()); - puts_fp(fp, " % "); - BN_print_fp(fp, c.get()); - puts_fp(fp, " - "); - BN_print_fp(fp, d.get()); - puts_fp(fp, "\n"); - } - if (!BN_exp(e.get(), a.get(), b.get(), ctx) || - !BN_sub(e.get(), e.get(), d.get()) || - !BN_div(a.get(), b.get(), e.get(), c.get(), ctx)) { - return false; - } - if (!BN_is_zero(b.get())) { - fprintf(stderr, "Modulo exponentiation test failed!\n"); - return false; - } - } - - // Regression test for carry propagation bug in sqr8x_reduction. - if (!HexToBIGNUM(&a, "050505050505") || - !HexToBIGNUM(&b, "02") || - !HexToBIGNUM( - &c, - "4141414141414141414141274141414141414141414141414141414141414141" - "4141414141414141414141414141414141414141414141414141414141414141" - "4141414141414141414141800000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000001") || - !BN_mod_exp(d.get(), a.get(), b.get(), c.get(), ctx) || - !BN_mul(e.get(), a.get(), a.get(), ctx)) { - return false; - } - if (BN_cmp(d.get(), e.get()) != 0) { - fprintf(stderr, "BN_mod_exp and BN_mul produce different results!\n"); - return false; - } - - return true; -} - -static bool test_mod_exp_mont_consttime(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM a(BN_new()); - ScopedBIGNUM b(BN_new()); - ScopedBIGNUM c(BN_new()); - ScopedBIGNUM d(BN_new()); - ScopedBIGNUM e(BN_new()); - if (!a || !b || !c || !d || !e) { - return false; - } - - if (!BN_rand(c.get(), 30, 0, 1)) { // must be odd for montgomery - return false; - } - for (int i = 0; i < num2; i++) { - if (!BN_rand(a.get(), 20 + i * 5, 0, 0) || - !BN_rand(b.get(), 2 + i, 0, 0) || - !BN_mod_exp_mont_consttime(d.get(), a.get(), b.get(), c.get(), ctx, - NULL)) { - return false; - } - - if (fp != NULL) { - BN_print_fp(fp, a.get()); - puts_fp(fp, " ^ "); - BN_print_fp(fp, b.get()); - puts_fp(fp, " % "); - BN_print_fp(fp, c.get()); - puts_fp(fp, " - "); - BN_print_fp(fp, d.get()); - puts_fp(fp, "\n"); - } - if (!BN_exp(e.get(), a.get(), b.get(), ctx) || - !BN_sub(e.get(), e.get(), d.get()) || - !BN_div(a.get(), b.get(), e.get(), c.get(), ctx)) { - return false; - } - if (!BN_is_zero(b.get())) { - fprintf(stderr, "Modulo exponentiation test failed!\n"); - return false; - } - } - return true; -} - // Test constant-time modular exponentiation with 1024-bit inputs, // which on x86_64 cause a different code branch to be taken. static bool test_mod_exp_mont5(FILE *fp, BN_CTX *ctx) { diff --git a/crypto/bn/bn_tests.txt b/crypto/bn/bn_tests.txt index 0d353d9b8..d99e1bdd1 100644 --- a/crypto/bn/bn_tests.txt +++ b/crypto/bn/bn_tests.txt @@ -9714,3 +9714,264 @@ ModMul = 6e99aa5b8107399848cf24fbd88ed6350efb68d737e505b466 A = ca6c51ba2f410d09bf71d60fe B = 8bdfa8fe5ef3b2ad02bc63c4d M = 84daecf412b8c50ad6dfdb546c3eb783dcc6f32003eda914bb + + +# ModExp tests. +# +# These test vectors satisfy A ^ E = ModExp (mod M) and 0 <= ModExp < M. + +# Regression test for carry propagation bug in sqr8x_reduction. +ModExp = 19324b647d967d644b3219 +A = 050505050505 +E = 02 +M = 414141414141414141414127414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 + +ModExp = 208f8aa0 +A = 86b49 +E = 2 +M = 30d26ecb + +ModExp = 27308229 +A = 17591bb +E = 6 +M = 30d26ecb + +ModExp = 2bdf498f +A = 21292626 +E = d +M = 30d26ecb + +ModExp = 11317167 +A = 4a655df24 +E = 10 +M = 30d26ecb + +ModExp = 2e1b88e +A = da6b761a86 +E = 35 +M = 30d26ecb + +ModExp = 20a12ec3 +A = ea811 +E = 2 +M = 23bc042f + +ModExp = c42ced +A = 1011a6a +E = 4 +M = 23bc042f + +ModExp = 4637d79 +A = 28d9a601 +E = 8 +M = 23bc042f + +ModExp = 20e5669b +A = 72fe6bc20 +E = 11 +M = 23bc042f + +ModExp = 142ab9e3 +A = 9a07b9363c +E = 29 +M = 23bc042f + +ModExp = 14c64646 +A = 822df +E = 3 +M = 30915765 + +ModExp = 160e35a2 +A = 15ea542 +E = 5 +M = 30915765 + +ModExp = 2f23a488 +A = 34d2e02e +E = e +M = 30915765 + +ModExp = 28e67f93 +A = 636a32703 +E = 14 +M = 30915765 + +ModExp = 29bfeaa5 +A = c8646998e6 +E = 2c +M = 30915765 + +ModExp = 30959e22 +A = 81dad +E = 3 +M = 326dd68d + +ModExp = 1a1da4fa +A = 116adb9 +E = 5 +M = 326dd68d + +ModExp = 272bf0d8 +A = 2d21ef08 +E = 8 +M = 326dd68d + +ModExp = 29f5054b +A = 76989850a +E = 16 +M = 326dd68d + +ModExp = e6c7b77 +A = b88ee70d2a +E = 3e +M = 326dd68d + +ModExp = 369605e1 +A = cf26f +E = 2 +M = 3ce082eb + +ModExp = 168a3c5d +A = 1f82caf +E = 5 +M = 3ce082eb + +ModExp = 125c4bb8 +A = 2e9c4c07 +E = 9 +M = 3ce082eb + +ModExp = 1c5fe761 +A = 523ab37f1 +E = 14 +M = 3ce082eb + +ModExp = 21703009 +A = dc832165e8 +E = 20 +M = 3ce082eb + +ModExp = 1228d1e +A = a5555 +E = 3 +M = 24665b27 + +ModExp = 5226af4 +A = 1077bd6 +E = 4 +M = 24665b27 + +ModExp = 1b14eac1 +A = 2db3a834 +E = f +M = 24665b27 + +ModExp = 161727bc +A = 6bd962cb6 +E = 19 +M = 24665b27 + +ModExp = 10d61d0d +A = c10caed407 +E = 28 +M = 24665b27 + +ModExp = 233da406 +A = b125f +E = 3 +M = 33509981 + +ModExp = 24032799 +A = 1656b7c +E = 6 +M = 33509981 + +ModExp = 129ecebe +A = 2e671504 +E = a +M = 33509981 + +ModExp = 20c20bac +A = 4d7a2de44 +E = 1f +M = 33509981 + +ModExp = 2e3ce9d3 +A = c53b3def4d +E = 31 +M = 33509981 + +ModExp = 12fadfd6 +A = b4cf8 +E = 2 +M = 36e9d4ae + +ModExp = 457ac85 +A = 1b1c7e9 +E = 7 +M = 36e9d4ae + +ModExp = 31debef4 +A = 3a973028 +E = d +M = 36e9d4ae + +ModExp = 2333ad93 +A = 552b97c45 +E = 11 +M = 36e9d4ae + +ModExp = 99ba1fb +A = 8bfb949cbb +E = 28 +M = 36e9d4ae + +ModExp = 27b691de +A = 93492 +E = 3 +M = 298fdb16 + +ModExp = 3c2b70f +A = 14e7b0d +E = 4 +M = 298fdb16 + +ModExp = 1486cda7 +A = 29acff81 +E = c +M = 298fdb16 + +ModExp = 11725275 +A = 507489205 +E = 13 +M = 298fdb16 + +ModExp = 24d14627 +A = e71c55606d +E = 35 +M = 298fdb16 + +ModExp = 222b8d14 +A = 9b1a0 +E = 3 +M = 3db59d12 + +ModExp = 3b8bd47d +A = 13f4e8d +E = 7 +M = 3db59d12 + +ModExp = 17e72356 +A = 334774ce +E = a +M = 3db59d12 + +ModExp = 306447ca +A = 47079ddd2 +E = 12 +M = 3db59d12 + +ModExp = 90bef3b +A = a75d62616d +E = 37 +M = 3db59d12 diff --git a/crypto/bn/check_bn_tests.go b/crypto/bn/check_bn_tests.go index 9e710d8bb..d49c2b768 100644 --- a/crypto/bn/check_bn_tests.go +++ b/crypto/bn/check_bn_tests.go @@ -217,6 +217,11 @@ func main() { r = r.Mod(r, test.Values["M"]) checkResult(test, "A * B (mod M)", "ModMul", r) } + case "ModExp": + if checkKeys(test, "A", "E", "M", "ModExp") { + r := new(big.Int).Exp(test.Values["A"], test.Values["E"], test.Values["M"]) + checkResult(test, "A ^ E (mod M)", "ModExp", r) + } default: fmt.Fprintf(os.Stderr, "Line %d: unknown test type %q.\n", test.LineNumber, test.Type) } From ad6d33c70dd18bdd90f0dcce3324b680a307adff Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sun, 3 Jul 2016 10:13:21 -0400 Subject: [PATCH 36/40] Convert test_mod_exp_mont5 test vectors. These were generated by running test_mod_exp_mont5 10 times. The values with Montgomery representation 1 were generated separately so the test file could preserve the comment. (Though, at 10,000 lines, no one's going to find it...) BUG=31 Change-Id: I8e9d4d6d7b5f7d283bd259df10a1dbdc90b888cf Reviewed-on: https://boringssl-review.googlesource.com/8611 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 74 --------------- crypto/bn/bn_tests.txt | 203 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 74 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index f2a33ecb7..6cabbd082 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -102,7 +102,6 @@ static int rand_neg(); static bool test_exp(FILE *fp, BN_CTX *ctx); static bool test_mod_sqrt(FILE *fp, BN_CTX *ctx); -static bool test_mod_exp_mont5(FILE *fp, BN_CTX *ctx); static bool TestBN2BinPadded(BN_CTX *ctx); static bool TestDec2BN(BN_CTX *ctx); static bool TestHex2BN(BN_CTX *ctx); @@ -172,12 +171,6 @@ int main(int argc, char *argv[]) { "| grep -v 0 */\n"); puts_fp(bc_file.get(), "obase=16\nibase=16\n"); - message(bc_file.get(), "BN_mod_exp_mont_consttime"); - if (!test_mod_exp_mont5(bc_file.get(), ctx.get())) { - return 1; - } - flush_fp(bc_file.get()); - message(bc_file.get(), "BN_exp"); if (!test_exp(bc_file.get(), ctx.get())) { return 1; @@ -693,73 +686,6 @@ static int rand_neg() { return sign[(neg++) % 8]; } -// Test constant-time modular exponentiation with 1024-bit inputs, -// which on x86_64 cause a different code branch to be taken. -static bool test_mod_exp_mont5(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM a(BN_new()); - ScopedBIGNUM p(BN_new()); - ScopedBIGNUM m(BN_new()); - ScopedBIGNUM d(BN_new()); - ScopedBIGNUM e(BN_new()); - if (!a || !p || !m || !d || !e || - !BN_rand(m.get(), 1024, 0, 1) || // must be odd for montgomery - !BN_rand(a.get(), 1024, 0, 0)) { - return false; - } - // Zero exponent. - BN_zero(p.get()); - if (!BN_mod_exp_mont_consttime(d.get(), a.get(), p.get(), m.get(), ctx, - NULL)) { - return false; - } - if (!BN_is_one(d.get())) { - fprintf(stderr, "Modular exponentiation test failed!\n"); - return false; - } - if (!BN_rand(p.get(), 1024, 0, 0)) { - return false; - } - // Zero input. - BN_zero(a.get()); - if (!BN_mod_exp_mont_consttime(d.get(), a.get(), p.get(), m.get(), ctx, - NULL)) { - return false; - } - if (!BN_is_zero(d.get())) { - fprintf(stderr, "Modular exponentiation test failed!\n"); - return false; - } - // Craft an input whose Montgomery representation is 1, i.e., shorter than the - // modulus m, in order to test the const time precomputation - // scattering/gathering. - ScopedBN_MONT_CTX mont(BN_MONT_CTX_new()); - if (!mont || !BN_one(a.get()) || - !BN_MONT_CTX_set(mont.get(), m.get(), ctx) || - !BN_from_montgomery(e.get(), a.get(), mont.get(), ctx) || - !BN_mod_exp_mont_consttime(d.get(), e.get(), p.get(), m.get(), ctx, - NULL) || - !BN_mod_exp(a.get(), e.get(), p.get(), m.get(), ctx)) { - return false; - } - if (BN_cmp(a.get(), d.get()) != 0) { - fprintf(stderr, "Modular exponentiation test failed!\n"); - return false; - } - // Finally, some regular test vectors. - if (!BN_rand(e.get(), 1024, 0, 0) || - !BN_mod_exp_mont_consttime(d.get(), e.get(), p.get(), m.get(), ctx, - NULL) || - !BN_mod_exp(a.get(), e.get(), p.get(), m.get(), ctx)) { - return false; - } - if (BN_cmp(a.get(), d.get()) != 0) { - fprintf(stderr, "Modular exponentiation test failed!\n"); - return false; - } - - return true; -} - static bool test_exp(FILE *fp, BN_CTX *ctx) { ScopedBIGNUM a(BN_new()); ScopedBIGNUM b(BN_new()); diff --git a/crypto/bn/bn_tests.txt b/crypto/bn/bn_tests.txt index d99e1bdd1..b93e1f92f 100644 --- a/crypto/bn/bn_tests.txt +++ b/crypto/bn/bn_tests.txt @@ -9975,3 +9975,206 @@ ModExp = 90bef3b A = a75d62616d E = 37 M = 3db59d12 + +ModExp = 1 +A = cddd44f47e84b3276cc36a5c0d742cc703e61c4756168601fbb1b6eb598c161019562344dd56ab6f603d920a12c360b285e6496a3605a2f8d691c3598233ee9366b5f2692554893bdeb67b7bdaf35ab7273ac593145e26bed82c70ba5793bf4bc5cac4c80b01785d1496beede493806e4f4aa89fd8d41de80dd6d0a3e2742678 +E = 0 +M = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb + +ModExp = 0 +A = 0 +E = 8de689aef79eba6b20d7debb8d146541348df2f259dff6c3bfabf5517c8caf0473866a03ddbd03fc354bb00beda35e67f342d684896bf8dbb79238a6929692b1a87f58a2dcba596fe1a0514e3019baffe1b580fc810bd9774c00ab0f37af78619b30f273e3bfb95daac34e74566f84bb8809be7650dec75a20be61b4f904ed4e +M = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb + +ModExp = 5150fb769d5c5d341aaf56639a7bcc77c415fe46439938a2190283409692f29cd080bfe3433005d98d24718a03a3553c8560c5e9c8ed0f53b8945eb18290e1c1a83d919302510f66dd89b58acc2de79ad54b8a30d3e1019d4d222556beefca0821b094ecf104b5e4cfce69d2d520d2abf54f3e393d25ed3d27e8c2e3ca2e5ff9 +A = ead8c5a451541c50cab74de530c89376d9a55c723e0cac3c84b25f0093c08a2961e49ab48966361c42c9f99111587252d98395b76788400d75c66ef208ea2767a28d6f8dc3a859f39c95765d57f139e7fc14f47c908c62df051e7216d379f52028843b4d82ef49133cce8fe671ae179423ac8da5be43b01caaf425cd969300cd +E = 8de689aef79eba6b20d7debb8d146541348df2f259dff6c3bfabf5517c8caf0473866a03ddbd03fc354bb00beda35e67f342d684896bf8dbb79238a6929692b1a87f58a2dcba596fe1a0514e3019baffe1b580fc810bd9774c00ab0f37af78619b30f273e3bfb95daac34e74566f84bb8809be7650dec75a20be61b4f904ed4e +M = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb + +ModExp = 1 +A = 935561297d1d90255aef891e2e30aa09935409de3d4a5abc340ac9a9b7dce33e9f5ce407f3a67ec30e0dc30481070823f8542463e46828d9cafb672a506d6753688cbad3d2761079f770c726c0b957071a30876c4d448e884b647833befbcd6b582787bf769d63cf55e68c7b869a0b86374f8920516cf5d528f348b6057450a1 +E = 0 +M = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061 + +ModExp = 0 +A = 0 +E = bb552be12c02ae8b9e90c8beb5689ffefe3378d2c30f12a6d14496250ecce30317c642857535a741642c3df689a8d71a276d247ed482b07b50135357da6143ac2f5c74f6c739c5ff6ada21e1ab35439f6445a1019d6b607950bffb0357c6009a2bfc88cd7f4f883dc591d4eb45b1d787e85aba5c10ee4fe05ea47bf556aec94d +M = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061 + +ModExp = bbad67352704a6321809f742826bf3d1c31c0ad057bf81432abeb30dc9913c896c03e69eb1cde6b78ffcb320c4625bd38ef23a08d6c64dc86aec951b72d74b097e209ce63092959894614e3865a6153ec0ff6fda639e44071a33763f6b18edc1c22094c3f844f04a86d414c4cb618e9812991c61289360c7ba60f190f75038d0 +A = 855144760f2be2f2038d8ff628f03a902ae2e07736f2695ec980f84a1781665ab65e2b4e53d31856f431a32fd58d8a7727acee54cc54a62161b035c0293714ca294e2161ea4a48660bf084b885f504ad23ea338030460310bd19186be9030ab5136f09fe6a9223962bce385aaaf9c39fe6ed6d005fa96163fe15cdfa08fc914d +E = bb552be12c02ae8b9e90c8beb5689ffefe3378d2c30f12a6d14496250ecce30317c642857535a741642c3df689a8d71a276d247ed482b07b50135357da6143ac2f5c74f6c739c5ff6ada21e1ab35439f6445a1019d6b607950bffb0357c6009a2bfc88cd7f4f883dc591d4eb45b1d787e85aba5c10ee4fe05ea47bf556aec94d +M = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061 + +ModExp = 1 +A = 9d92629c1ab181c50c31619e8acd0d235a1f5fc7a0bef4d4fd54b4f1968d45921f8522efe88e69c6c14c576c564592b9feb00d1554b88b038934eaf4a8ce81a2582732387490181ef158360c8b2d9ccb326ffe043f776a50cb8202837f08ca743b562eefa007150ab7012c341b16248478d4775c02ad71ea13d5e82b71e2d600 +E = 0 +M = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b + +ModExp = 0 +A = 0 +E = 9f43dcb641f3ecf4dbc97450f2bdf3b7ec6a2f3e8e96bb1df2bf34b8d2d78e1a9018d04d960ffd0e932cfc60d3b9b923e3f9f29b3f3d61cae3a9f7245078143475c7fcb896ff200f7d94c4f2708bb42750e37c185a31c876814e4f06a00771707654e1da2fb69c16b6500b16385e3b933e2276ad3569977473f699b1c7926c3b +M = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b + +ModExp = 24eaead5b57883c2f454928f8edd470a344bfe07a953194f7d635d705ef13ddfc64140c8ad6f363d4c828e7c7891a6b6d4df37335de4552c319dafd1c06d1f743240082a3535df4da1475d3eea3fead20e40815fd5a0876c881c162ab65a1eda494280c258901ca953d1d039a998bf0e9aa09273bbef4865f3054663b72d75ff +A = a31618b4532f53729ba22efb2221432fab1dbb70853d6a1159b42fd19fc949965c709b209de106a652aa422d88922ce51dae47f7f6deaf0055202e13db79ee84fc3d3c6f4c003ef96597c49d6895fa53c22ac9e4819f7048146b5272f6279424fdb389819a0b251c823c76f4bebf4f1246de455aafe82a0d34454f5039e90839 +E = 9f43dcb641f3ecf4dbc97450f2bdf3b7ec6a2f3e8e96bb1df2bf34b8d2d78e1a9018d04d960ffd0e932cfc60d3b9b923e3f9f29b3f3d61cae3a9f7245078143475c7fcb896ff200f7d94c4f2708bb42750e37c185a31c876814e4f06a00771707654e1da2fb69c16b6500b16385e3b933e2276ad3569977473f699b1c7926c3b +M = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b + +ModExp = 1 +A = a8558e7f455b27c0c46d7d0862eb409cdefbeca945e0284b5bf425b7ac0f3d316bc365594cc1639decffc621214d61479bc75135120d4ac09ea8b742ad7ec1822091b62b1c6f564fe5e2f4f5b7def92cbaaa9a898549207ab01b91c2324fbd306a87f7d6379b6fb6493c5fca76729767f136120da9c90bdc7d364f7d242d5acc +E = 0 +M = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f + +ModExp = 0 +A = 0 +E = a5524b41dfc6b570df1d8f6633ac7777c1131abe3a99c6166b0d29d3b8883c41b00a0c53cdd6f42820bf05c810b6ec53e77a8c1b9344ea0c91d4f410a2f204c369f3db33bf8c88217fc2cf802a9d9bce8119242d8e781875b85431be170076498c0963574ee423551aec9557e2fc672ab1ab5d0cbb1c400535df9481e7934d8f +M = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f + +ModExp = 292f0b39ca0f1c850b1a00cffd2d54924fcd5fc7e7504c9d593e6c0ff74760b1f4bdd81679fe06c50248336f3108c593fa111072ee87d0fcc89a63243a1dc89044503663eee9bc18f51c3e0193d9108303e12ac90ff78f6ec752a4386af09c42db524a7cbe9a3d4fcccd56c34d283bcc9debc17158b5fe8df0c1888a9841bf8f +A = b4fde2908745ff92cc5826a27dcfdda09e8fffee681844fa4c7f1354d946d5d84e0e0c7a4a4cb20943d9c73dd707ca47d796945d6f6b55933b615e2c522f5dfc33e0652917b4809bab86f4fa56b32b746c177764895492d0a6a699812b2827fe701d40ef7effd78ea8efe1cac15ff74a295a09614bf04cae1a5017872ba22efe +E = a5524b41dfc6b570df1d8f6633ac7777c1131abe3a99c6166b0d29d3b8883c41b00a0c53cdd6f42820bf05c810b6ec53e77a8c1b9344ea0c91d4f410a2f204c369f3db33bf8c88217fc2cf802a9d9bce8119242d8e781875b85431be170076498c0963574ee423551aec9557e2fc672ab1ab5d0cbb1c400535df9481e7934d8f +M = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f + +ModExp = 1 +A = e2845c572b46496ac158a731f612fd40ef626fa7134755c25b1b7614f4d7b29164e6142ddb7985e4c7ebc575855ff901e95927fe98a5aea2ad3a4720c75782323bea1518b2c57790f44efd9411be4e95b3896bad1e73c59658290b309e5a7eb5ef8be08125063e57336b80f17eacee88966d12bbaaa15a25929c82e027cf696f +E = 0 +M = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d + +ModExp = 0 +A = 0 +E = a55703a72ca3f6074b939ed3d748196a684a3c8e411c2b39a9beb98993b6eb7ea3fa16f41bc5b5c3710b91c0fc74a8072793052f872f61695db3a2df872eaa427a110f1a8d568c85d58bd350d0df8eced7a10be80f7567360c1a8047b9c44aa2967cd0d9dd2caea2c1492358c2db4f0214da343fdf2e34272865dc5c63be2ae4 +M = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d + +ModExp = c90e4c69df92e26549b016950b59080947f5403430698e128477782480dd70be96bed2b9042dd8c708eb432e02710555b97af11ce6fa9b53395022851c32d1f53f04237fb0763563b440ca6e81a50d909d907d9c26b7d3c420dbf88f7dadd488666848135f8cdc608dcfb0691989289fb54379c2e84c262f9765f68c012ca1b9 +A = 882ea1b9b6c79a3b1bdfd284658cb6227ad825e0178cab713c7413c2ec34f03cfaec470c4f5c521f5e9899a2123878ff0f5b36a4196c08ad1b04d03746c4bfb5d126f5eefbfe172627d6732710a8ac8890cedbd4fdef69a19f2b3253a5aa0e5dd5484f72d59b17bdd1dad3db209a3ab839368ed3975069685911d7b35e41a9e6 +E = a55703a72ca3f6074b939ed3d748196a684a3c8e411c2b39a9beb98993b6eb7ea3fa16f41bc5b5c3710b91c0fc74a8072793052f872f61695db3a2df872eaa427a110f1a8d568c85d58bd350d0df8eced7a10be80f7567360c1a8047b9c44aa2967cd0d9dd2caea2c1492358c2db4f0214da343fdf2e34272865dc5c63be2ae4 +M = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d + +ModExp = 1 +A = d7a99e65b8af86b1c51d851f0447e43cd4f343cb0ada7236283e69aa7ebd383826acc9809e5dbc4002d0f2430022cb026458189db3805ce2de1142a31ba71a6c064ab51f0059eb4b931b8bcbaef023c38d57aa5f3e14f5df77e547fc028702071b58bd57338be1e1e4f98d3553484e4de359cefa29c5f58d3fa5d823f389dbef +E = 0 +M = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d + +ModExp = 0 +A = 0 +E = 95793fe33696f53e37498b2b65aaf27079e27acf1da97dda2c3e0803e8a02139f574e04ee03f7d1ddd029f528e3f3644515ad6f10f0beac2767f23d9cd8a8b9b6c6e376e36b64a0ae2711d7d31a5a75011641935b503110edbefe9f0ff2da27b5c5f6bb8cc151fdc86f67191bb99160c6cacc86ca368d5bdfafd3f3ff5161b1e +M = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d + +ModExp = 186c50ae259aa0fd31859cbcfea534e626a254de33956d5d719334bb32e7cf37cf199a21f079a5b90497228994d05efe19ccd8c769cd81f896286e8ae557cacd1630a928c629ecdfece29ab3697794aa707734e007318fa7029b050bb09ebbe6986187c6ca843f55266d275620b3f0fec0ad5f847ce8b314d929d128b33a249e +A = 9d5e345793faddca9867f23eeddf6816c1e837f7a2cf96fa077212514acb6be87ac01a237d8f2f1d07d27a8ddd1b0ae0d97e1bda4f205a89435017284cdedea3e407b1b940d6f52112b6359b3e86e4c83074b17c210ae2c8856b42b169b4a7a6dfa65b368a7959496cf9bb1ee93d019dbd79101830e3f5ed08604ab90890b914 +E = 95793fe33696f53e37498b2b65aaf27079e27acf1da97dda2c3e0803e8a02139f574e04ee03f7d1ddd029f528e3f3644515ad6f10f0beac2767f23d9cd8a8b9b6c6e376e36b64a0ae2711d7d31a5a75011641935b503110edbefe9f0ff2da27b5c5f6bb8cc151fdc86f67191bb99160c6cacc86ca368d5bdfafd3f3ff5161b1e +M = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d + +ModExp = 1 +A = e6a079bdf7b0638d50b183475e9ddfd5cbdebfb29f5fae8e9be402a0bd36085737b556492ea7fb4b1000ae9ce59db66098129b757cfb29224275fdaa46b8b7eb18a93ca7d3e446dc38c734b683d7ba7927b008d993aab01f44239d3c76be76d1503908e9b5e73b36c43ae0771368b01f39c042693bd92c4fc50810f059e1b332 +E = 0 +M = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247 + +ModExp = 0 +A = 0 +E = f0460c5ca9b3a5c2d1b93c201d020dc43e1c81d1daba432e2cd310902da23eb81a5172b0b357484eb8fa2c04c270893b8198c8ad35453405dadaf05195b3aeb5ec0ccacecb4b6227ca43b27b97e240a4148a472670ed60f304302f757495fd4a91af0fe09800db0c3043a6ae213bee6703ad80523ca433d99ca0eab1e0b7c929 +M = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247 + +ModExp = 60719701a2dc0bcde281a93ce0b8421d1a718adee43c1b5d9fe9e697a48ab3db4f9f33c73cff305ab6b6c300c149b05c6b289dce4580860dc56bc59de81ac074ecebdc65aa3ca040b44e5b3c80ddba1658d78b9abbc4c77e5f171f5582e70ab4438a8e1e2f062d618c4ad09c70c73b5b5fbc9f8f0bbdf1d530a933b705f85af8 +A = e1b400cd3b1f2f1c6b437adfdb970d2c8108f1b39bdbb13582179552011c6c97cba6bff2c463212b7f62776aa3e3aff9f175990e79395e819c144350b0a23d61638d500ecc97726b098e1af334aece23a851c718612442c04eb7b3805a24cc8f5b90042145eb5e5d6a408092832b6bbeb8a621419a9282fb5c075f41c7f1fdc1 +E = f0460c5ca9b3a5c2d1b93c201d020dc43e1c81d1daba432e2cd310902da23eb81a5172b0b357484eb8fa2c04c270893b8198c8ad35453405dadaf05195b3aeb5ec0ccacecb4b6227ca43b27b97e240a4148a472670ed60f304302f757495fd4a91af0fe09800db0c3043a6ae213bee6703ad80523ca433d99ca0eab1e0b7c929 +M = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247 + +ModExp = 1 +A = 9dd1e6f2d3ff24096b54e0ebf0f10e283e484a1cbafc0431adda1296ed97692f3ba99440fd4f67c96dd8bab850e1123361c99362df9ea205ff8e90d1b329459f54730992d5a360e46fcc5f5a909e691abb9a06613d6991bd7c2aa609f0d7b441d7ded0c07b8c394327672d38a905efb2d76aa3be5bb14d0c002aa37e287aee79 +E = 0 +M = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f + +ModExp = 0 +A = 0 +E = 8622c37631e428402343dccf8ed09d47b3f4201e95058910289a62707c3ce0b7113c390056cc4796cc9893e471b12cb3f63f900f3356ffd25c8b2fed6f6a7fba2c684eb241ca706c76cecbf72473d8a58c02338e40714b5610465cc319f0a529a7aa3898d9e638b247abd1380c6e8f7fa210c9f1a1a2164db6db83a6bba79436 +M = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f + +ModExp = 86fb0b8dc161c41de2adb0f3ddcc8ad49c1efd729a52793a3ac987d4011c9c1dadb18657dca718df75c8ddcc49d60f152c46ab85ae9076ee7bfd405679a7da3a5195a1bbfd7d2b998c7b135ea91f8c445cbafe1276fa502c2a85477716829a2e0d24ba02623405a3654bed8f355bc7ccdb67c3f9a01e249e358b60d7699498a9 +A = 816610e6018ca47074d55750dd16a281019dbf95dc752605794cbb8ea8d75775317ce685737859728320b529fb3b4414b40bf3a93d08d8994a21ae54682cc1c357eb529837a7b0129a0843eebd9341c9bee3a8ae30475bdbff517e885a0c9f2b6a680643bd981efb53bf9dd49f3dc3cb757e117895fb34b1b4336d9bf8384558 +E = 8622c37631e428402343dccf8ed09d47b3f4201e95058910289a62707c3ce0b7113c390056cc4796cc9893e471b12cb3f63f900f3356ffd25c8b2fed6f6a7fba2c684eb241ca706c76cecbf72473d8a58c02338e40714b5610465cc319f0a529a7aa3898d9e638b247abd1380c6e8f7fa210c9f1a1a2164db6db83a6bba79436 +M = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f + +ModExp = 1 +A = 9edfce4691f46eadaa2043c7b1092b831ed50f3429f0bca02f985c0b77c686d951be84d772ae4b55f08935bed6e3206c8441574f215736b5c1c1b7595b3b789b55cf56db83741b10144d6767ba2b97b23a5e83504c60e06ab22834b0145655aa0463108317a379cbfc8a93de8a66925a999b8b02bf88dd85fb9898cefe9c95c8 +E = 0 +M = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745 + +ModExp = 0 +A = 0 +E = a3be10ef04535fca6784e5dbf3733d677dedd50fabbc3a860496628950b4747a328c2ce0d903cbe1e700f0af30f59fb917202257815097a2b516df5d0a82642faeffdfc3b7883766c78fc4be5901ebef891a9ca27f3bcf00960729e659bb3fddd54a19ce628e95ab86e4c7a168588bc9f67b05dd21a583acd8dc36e615945648 +M = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745 + +ModExp = 442866609915aa6f1bae9dfb59e721e1b63f42c0f75fbf0a88344120fbbd7aacf15208fb7c9d8bb8477d553cbd826d7e685ad764a8423e81c2131c040ee83a03cab8d5ce50866a941b48c78e9f1330794d908562d4141cfbf26e8c80c69551339eec41e37e2b37b54330f7bd75748f8d26d56ab9eb3b0c127540484c6445a7fa +A = 8ff65e2cbcbcd8697cc3ce9a26855d6422ac7eb4e66500648c08be697e005cc3c854a54cfab91d43489cd60be8b516a9b3c9688e5e009a1689c6b164a133859a5464ef422c86344fef42cc477c9df27768377c126a066d1b62f593b7f6d6e906feaee16addb7cfbfc043d741b7dc81a87c17f167b7b8ef1b1fb3dfd1eb14102d +E = a3be10ef04535fca6784e5dbf3733d677dedd50fabbc3a860496628950b4747a328c2ce0d903cbe1e700f0af30f59fb917202257815097a2b516df5d0a82642faeffdfc3b7883766c78fc4be5901ebef891a9ca27f3bcf00960729e659bb3fddd54a19ce628e95ab86e4c7a168588bc9f67b05dd21a583acd8dc36e615945648 +M = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745 + +ModExp = 1 +A = fe9f77f7d0475e00ec964c0effb9b8e079c32e376ce77a9c40ce4018c3df44a77b4f294d9565502b2b79accb30cb58dda6d15e1543b6d4a53296543ed11c7f51baab60283ef03fae37dfeacb431392487ec2839551a933895c4dbf18844f7b375d3e6f558d3c39993cea1bbf7fb743a6a07bd3753c03eb7298811476d7f3ff1d +E = 0 +M = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511 + +ModExp = 0 +A = 0 +E = a0bc148ed50a9b54036bb8fa1f214979052ebd47db8b347af3bb03b806bb457b468ba34781f8a25f289a7a90af4903dc14809a166df2f4c3527de2ea6911cb1afb9071a4afbb522a7d50634d66fd584c73f32d05217dc9f7f16394c68a692a953492ca85f89cc11da95fd8cac6231647923ced48a1b3b0ee68c010286d452836 +M = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511 + +ModExp = 91fd879d02f95a9f40fcd1037726f73892caf84e9b43b4aa4126d9062a0d22c464e7af2fbd91aa849612d99d9519b724a7fb1cb018fffdcff321d883ab2519953c9f174f09dd8f13ac87339887385966eb4a94842276637b2c36c0a5036b1d3bbea438bc6efd4b4851c7ec06879d60694df894717569bcd31c4b13d80df6cbca +A = cdec5edc1cb3ea974342b85aabc0f9385cf877ca328747d40dd4d297623ad69ab6582653faeed5aef225208305135cfbee32e066cb43e18afacea3a32acc8aabbc49617ac33e741651924ae56dd6aa044a12a1ea50fef573b5befb2f4b21b9cf83ab2aaa6fd153580a0761666ade8fb94f202a3c3dc4f33297eabb4564374168 +E = a0bc148ed50a9b54036bb8fa1f214979052ebd47db8b347af3bb03b806bb457b468ba34781f8a25f289a7a90af4903dc14809a166df2f4c3527de2ea6911cb1afb9071a4afbb522a7d50634d66fd584c73f32d05217dc9f7f16394c68a692a953492ca85f89cc11da95fd8cac6231647923ced48a1b3b0ee68c010286d452836 +M = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511 + +# Craft inputs whose Montgomery representation is 1, i.e., shorter than M, in +# order to test the const time precomputation scattering/gathering. + +ModExp = 9442d2eca2905ad796383947b14ddfcc341f5be8fec079135c36f6f0d9b8b2212f43e08bf29c46167ff0fe16b247cd365df4417d96cc31c94db1cf44b73b0ee3ebcc4920d9b0d003b68e49c1df91e61bc7758a8a1d2d6192ff4e1590b1a792f8be3a1b83db3ad9667d14398d873faf5d885ec3a2bef955026fae6dbf64daea2b +A = 3a4b4c57e62c5e9d1a9065191f8268fed9d5f6f424d071acef66f0662b8210f4c029ed991512e40c9c912043c816d2c4c5b53fa0e5c253e16808aad4225130dafbbb89fd4f30cdfc1c2f2179b636a7ddc4be579795820b4b9377637bd8a21a0ef5a90d0e0f865321eee23d9be2a3b7320b4012d02941b892df2c40bdc85c1898 +E = a2c56ea1362511cac0301918e15a9afe7d37edd438a5c3538d258ea01f0a6df758de07111e868b3ad8fc89b629b4955d78a1b3af902be1806410ddde25ccc6a196ba5949395c1ad5d8725b18815dc1cd5ac1c7dd17773f571e3f2e628255af14476e0494be23a4a4dfd18e23142f33d7a59c236fec61660e360d9676a747c69f +M = ede35a3a7afac817d413373a2032abbc067b1493f709ae6e1282ee5469743391d891b904938857168802b7872d3cd7ac18ab249a9e540a86f970b1d0f310a4cc29df1cc9d4063d98c554f1a32f4ca5eba3523cdfb142e0fc609907c7a92bb0187009d97ec471db3545f42dd5fd29c07b7816085d09477ba31fcf90084660116d + +ModExp = a7f5844fa9e7202d4b70ee252c9846e63d3d091b0387768ded872cec53458e19df0d9b4960226e269b8ca5dd4c4eda423a67b6dbb48235c08c12c6c7c78db47287756d3ed9cecb9232f7d18d5d80b9676cb68ba4a290c97e220beb1a069976b5e6022a4c1e5ddbeec86b62dda24ffea1deda37695c9f61a8817218e6370c0679 +A = 7d6d0cc947ceb949cdc4e9e1044f5deca5bb05a491041e0d85bc4b92a0944a57c72845fad91e59010c61ad1712bd2f612d53a846a044632262a9f2e3373b062fde2484e0c165ff947f2469f743ab6e2e5e13c640fc4029b1c9213eb8473c674e7f9e95a4a5c5636d4656c1e696962340d77b322daba47d6fc894f2a2cd9e0afc +E = b78012afe806e2344d004c739c97324256850980ac97d88c4ed9a838517639ca112e235978d21a176c33f5a68703aba0f2a05501bbe3fc8d49a000fbf530cdb431581dfaf8683cb15a2aee5e239cbc542827100da3b47babf4a16ca7c588aff9912e674abb449e0b767a15e415f4e7f2bbd6380d7131da3df8d49b13bfd35ce3 +M = b72d5c55bd2998472f1965e75a51be6155c1ba04656da8f66bcb34db36a7b1db66a89d1d05b1bde10206acf85be7b474ab689220faf1bb52ab39d8dc00512dd4e26df1179c11b973e1274db85a88c7cc2a17113abdffe58cb930ddc5f3ccc4d68b4e65c913730509f7ce5656e8bbaba9b1be177ab9f766678f018fea05da9cdf + +ModExp = 465ff295786a88496828fdc763e9292d557957544e9322b7996807b87fdbfa7a11614bffeec557ca831c4824c8e4ca3b1a1c7f3f4f95ec3fd6a86b73bb13d78b73af2b3c7e76954d0cc03bcb0cd606867ebb3765a8b3d0108cbe4f343a14016be9c33f6d200f0dc547e7d6b02bfab1e79dcdf9c9835a814cc6c855a12ebeb66d +A = 89ad02bea3e9ab839a6e23f20122409daba52c68e1e893034b30d321c0305434a6af940015e3fa5ca9c35230da34beeb1ed4fbce6c1da3a8bfe3f3ae172276c1d1723b47ee61e6f8fcfdafad102d6f7ee2a79f510c7edb93096205a40a6c9e665b88b18f39a979e2e61286d939952a6f02fe8148b7515bb25f4252337cb6e60d +E = cbd6ac628cc7afa3c61bee9c22a06a395087ec1811fe9681b55216700c435996c815e7cec8aaa90016dd2382d0306a5414630124e14f3d396a4ba02ee17851bf720f1607ff813e4bbddf01338983db12f59bd6371a738eee3eeb716f21051d6174d2d6c77602942b9edaac18d4b3a723096c0d00dd23a8a605c585022f311560 +M = fa7a3e40364c8a8d0f14f0213a3f3e035222ca0ea19d46d10ba41580e5dd2805c8a133f3856d7d5d97f922ea540e5eb0d10ad04dfdbb74f518f58da0099a6fc2b3f3def92985176e07fc78aff2faebccca10a429794e5f15ff92f75fe90f527c60ddea8093a9078c703c372ca09f7aeb27ade02f3595308c61dd9c44e62fd101 + +ModExp = cf08bf00261402102e9fe03f3074471dcf0e9b3c96d4d1503f099f24ec85e1901b023e9e048c1ad042244f5f70b38b25a99f4c0a7b57d5844bb0d0137367f45f4ce2cc7746105b77414768cb97648dc5721149aed2d4c682408cc0d50d26dd0bd77e848911f8625c727cac5f32e63bcb548f41a57d718d772f23983a42f603bd +A = a419646a6631c2c69b18f7aa65011825eb31692eecaee9d74f92d92203811b68e9764bda31a1585bdf69b6273fc6f9f508c395ac081336506525dad88473512f08a205621ac8b16e9864c7a7c5a4f17435de00d0b32badec6ce4897e3e1076c562b6d9523f63d0b2079eaa416cb090471657763f24931d955d1fa2720c80a9c9 +E = d5a6f4a1842aaee39805356dc8d0d678ee03b2c81277345beccb2742f899132feb43271f95968a01ae68aa8277201851992dc0aa7a71c90aae71b124d873ee264ea400fb131be0fc6c4ce8c04c45f6bdaca89ac743635caf6158983d257e21cef6800d7f990e912ba21bbfb8fb779afa4abd19e07e7e07eee9908493d1ca502c +M = e739689b6cc6def1d45fb1a2ab551643beeb303f4aaa4da47ee5e4948510f8445b4c40e99ae8354dede60b2ba6694e93bc4d573b7e8adf871b7a9a9636eb7d70f2e49328e2d7978143b177cee8374ef01bd1ee2d95862765883f5e7971668b53ef0ff41b6539faf63c397522b0bdce916388e72e26c8d3d2e58dadeb9eb5d479 + +ModExp = 827e6312ec3b14600203bb83f5b277ded197b2967363630ef673240df05edd3ba8ab2b11c86251a612206569c6c33952b31e264f129909bfe723bd0ee1624b36cfcfaa893a6ec8b5a1f7de79f83e79b459a3350f89f412ad1cfd6bc4c2a7a29272c783d6ecceeb1398fa17041835643f4debef9b5e87b098d104bb8912dddf7c +A = b8e49c637829021d32db3a39a0c1e58cdd4c6e4eda7e8e9293be379e9c2e2d184f929d278598a81ae231cfedcf69cce4a6e31cda3c8ac14d753a7311f2436e29795f0dfb60259a0f61a997918ff984aa2284b43a9d64c974059e9682adfffd018305835f74eda8c75fe4877d811c1620f654ec9f7f32d1af5ce59115e2f41785 +E = 80e0febf369d234bf1aaad4f82df2e2ff02882c3184781f6ccdf4f7cd93b6887af86830077c84dfb02109ada05b40970b1c65228b0c19030bd6361c3537fee22a8155c03b4e7007ca006c6daa3659518d05bb81ea0079456d0ef6116df248dffdb0c935f321f5a1034deefd5a9414a0652aa6548de33325b474b9e5a8507a082 +M = d5eb1d14af842a9973274f7463d90cf0ccff19c47d710edbae184478d4f29b02693ed7958bd487054327b9e6d8879e24c9af7730b92f323eeac05558da6c1b952e5dbf13de236050a77628bb5325fe0d14cc5773bf73338759d5ab43c212b414581280f1cee250007e53791b800b61c90de0328acd7bc43fbdda48158939392d + +ModExp = 4a1efd29c7e78549f5cd4deed1454b37462c7810ee6a8a2493b764dfa479be13b314cf9ff98259517d61865567ef499a511630c0038c97914625df181c6fe07892f329f98b344a78d751e9471483eebaa7977371bf97bb25187ae7e93a9227d6c124ccb4644423c961a11ae59c4354f89d5a95164c23d9aa256e289e9cc0858e +A = bd86c9211fa6a47a06e5016c46cb8a99e34a043a29e22f8c3196fa7197c26b38927b8d9bc0ddc11a5fa4bcc44deb69dbf37cbe7ebc9a2fad6c74e09ab5a9dd929fa04ab4319b6caad1035739be78ba631fb0748d9e53944836d37ccda6e6a62823c696d8f31139ccd7f2f86b22fa026ecf433cfb1271a3539ac4f1c83aaac059 +E = c40b9972006d28a84c2769a86e526a2b274f73afc7c5c6a2742166757f61b5f5fdbb228afa157af62af989ffe966f232bba9e6beef5403d1690ade31a6410f7f349a35bc4267a129afd647993df7d45cc0e1a1ba4678d7f1b6e8a344d8ff7037679e1f4db25a454e4246f6b55c416567fcfa188e8a3865115851d9edf0aa8902 +M = cf424d7af75ce7eef90cad75ae55ca8810cc7b4703fdb5bce701e7bac07e0c371cae06df2aa8facb55a0faa6793e4d2bd9d7969703743b9be170be82792aeea55e2bc0f7ab7617b276486bf474dee2f4556aab595ff3ef115139cfe5e21ccd4ee05c0e1cf901bd85df86cc17195a783b0be836d00bee82ce064077f9191188f9 + +ModExp = 3137a3049fd4ad2e26d870f5c998cf11bfe82101884a82e85e43facd0928cd7434a2e346ca124619769fa141bbe92ad6f36b99231032ddaec3b349a410f82b5ca36f45e56e5fb85dc63d32053dc90805d3f1854ab385281a71a57726bf97158494e7476057214ca7379ab8b70f5bdc15f70bdad3adf33c3a1f9cd1b6bbbad556 +A = 39a1dc6a4c3f14d9c350ee968d5ce139ef725952c967a2d1bedf48ace22091283525be03807e2e263d2640be77f0525247bcd07149bba50568cec5a082c87d72962cf9e43bcb5cdb1e7e9a650fb53e0ec2fad37f09a9f036c0d7dfa528fef846769f80a9a60854910ca1b4ee05dba82ed2ee018348d6b3e52a764b8ffae61e0 +E = deaee3a3f80c9f684ed7110c0653847ccc7be5ff6d982fd4b49f59b5dd35f7210b1077babbcedbc127df35cd469dc6e569a0f84e58149b5605c94b09fd7f0b098d02b4a04631328b3fae39e6c2fce25334225cab71829abdb9507cb903701559660f2c08c3b743336119d1260a0db27054cad3f28bc1b04b2289baa58fb33965 +M = 938388927d06ed3bb1286c0f06d3054cb0ee16dc7a0bbbf13a45293c09a5f40f1d611b2e1a1b0ec2ef109b508e27af4274954905cae52034f8740a744153b4d22059f0dd262ea51785522098ecacced6da07709ee6b5acc8c4e99331379a7c3de7f4e2d1431e43b19570140955b7bcba118dfbaa552cbfa2be531e8f781166ed + +ModExp = c15ae334455d9f4d1030cd33e734726a27c63624c2afc576238cce5e0498298a4a0c93090a0d19568b41290303c4b558f3d9dd74f9cde8798710f68569ea0d6fd971ce67ec5b54495031de3d8842b8b49288725bee5c9f72b99054d64986ccd4e18d70d5f33943f08cd694eff538f84438ea993ebaba0910c95b3a694f213510 +A = def633b955a917569df3ba8517455eef0655e7a35985edda27097a063e0d82c7c3a76dc36c5d8a71ba9d540790ddd0ea514aaed98925f9a1808eb288d387aaf9605a9ef8a333ebee7ad7057bca012efd619d5867f02266f65976ef4b16da17468426ac4f99b3e8921707e01b4de20f6f9a068e6a19d872079a27f3a44449db83 +E = a465c47b0d15d48e01bb8b1d8e3b3253e11515f6874dbed6c25818adf1a8fd927124d5593beb367f685c11e46f18415be73ccdf16fa2e93a600b728163d21d232849e5278c3749d903edad3f1c4535a2f55a2ab65e7ebc64888bd2a0527e876ecf38cec3ab1980d08138709fad8eb88ae65d960adc3f0f8e92f784fe96fcb693 +M = e43cb9ac1446154356cdc31ec771c79b0e461e22d95185bbe1a279c0945e3af07903a0cb54d553380716fcdcafb4b7cf5dc6da481dc74a8c583d75ff6c1f8e429182d200246ebc473bb56e173787987c1b7fb2dd23f5b2e438a97bc4a1df628bc044fdd1e80c0cf37030adb7b04784dab827d0dcd64f0dbf37c980612570ce11 + +ModExp = 75c3f79ab7c991b98e65505342a8a563cfb08b5d3ccf8664c7db1de50256b1d17ebf7096dc98c7bb5d7f027a894ae5cbb14dee04d5d445e775ad7e239acc82673b0ac2d819a69c83864f34e73d9a636f05de8279619a067b4c90ad038db5910447e03841d2034635018f08cbcd21efa00994247763a249082594128112f95232 +A = 34def7d76f6f158a359fd12759fb889cdf6af0a24830dc3e84283a1ab4e9b2647a6a36b86482f829b2cdf3e3d6028f9a884b1f64f7262315446bea8b0231828e2f3d990fb103c17f820b39e4b8427c85643ceeca8f5dc8f191d1255768300e859bd7d88c770319ef38269660d221cb3bc061389b6fc0783485ef042b1c7d6fef +E = c6c46453dd5aac6b37277a446b1d0c69cbe476eeff55b3ac35edb89ba97116b0e7783660f2c7b31b2a2d6c4709d0ab45d01a838100694b0777c9c9c14c959b07c437c73a5eabb7402f1001e802d797a2e7707285834fb6440a1c2f727f7bb84ddb2a49312d32fa0ce620c43872655cb5c394749c9e75d7fa25be00efe50d47d6 +M = fbbab6698a9142095c46b38a732592e4366c1838b84bf40f8c8fc7b630f73380a0d09765562365798f8c8030ed1b6728329d8bb06e882c35a1d59bfe84146a9db2afe42a414014e247390281c782fce806d62adb54778d2bcb49555459429d6ed446af5359657667f6aa19e8e3e0e24ab2bc312b2d90b5cb1ce6f2f15af15d9d + +ModExp = ba16d7f3f6e162ce248490d164a13c00e7720d8a667e2d3ebeb13f1663e15ef5408d5b56cbc7bc793a8ca787cc50f8e15e0e9d4ee764531d04a9114eea556bb3e206ed7d85267151a056b6e68fbf35e03f2cf829708ffe1de13e95ecfe365aff1eea36340ffcd3892dee659fb1ecbe50f5080e54737c10f9c1ba638b14ef537e +A = 9025e6183706105e948b1b0edf922f9011b9e11887d70adb00b26f272b9e76a38f3099084d9cccf12d04b1a99c0f654f8b9ed90c6dff9478c60bf05d58d734ab60eaefa14a22230ec60c90dc1f0704b61eef0bef345785ae0e6a9af7db069cf6bd2b4e0fe58a0ade83c7e46a04b9fe1d24cb9b65c6f80de713e61d70eae5b286 +E = d7e6df5d755284929b986cd9b61c9c2c8843f24c711fbdbae1a468edcae159400943725570726cdc92b3ea94f9f206729516fdda83e31d815b0c7720e7598a91d992273e3bd8ac413b441d8f1dfe5aa7c3bf3ef573adc38292676217467731e6cf440a59611b8110af88d3e62f60209b513b01fbb69a097458ad02096b5e38f0 +M = e4e784aa1fa88625a43ba0185a153a929663920be7fe674a4d33c943d3b898cff051482e7050a070cede53be5e89f31515772c7aea637576f99f82708f89d9e244f6ad3a24a02cbe5c0ff7bcf2dad5491f53db7c3f2698a7c41b44f086652f17bb05fe4c5c0a92433c34086b49d7e1825b28bab6c5a9bd0bc95b53d659afa0d7 From 4cb00ba08c653642320bbc8c10700d4e2f7203ec Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sun, 3 Jul 2016 11:52:58 -0400 Subject: [PATCH 37/40] Convert test_exp to bn_tests.txt. Amazingly, this function actually has (not crypto-related) callers, despite being pretty much useless for cryptography. BUG=31 Change-Id: I440827380995695c7a15bbf2220a05ffb28d9335 Reviewed-on: https://boringssl-review.googlesource.com/8594 Reviewed-by: David Benjamin --- crypto/bn/bn_test.cc | 70 ++++++++++--------------------------- crypto/bn/bn_tests.txt | 25 +++++++++++++ crypto/bn/check_bn_tests.go | 5 +++ 3 files changed, 49 insertions(+), 51 deletions(-) diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc index 6cabbd082..919940009 100644 --- a/crypto/bn/bn_test.cc +++ b/crypto/bn/bn_test.cc @@ -100,7 +100,6 @@ static const int num2 = 5; // number of tests for slow functions static int rand_neg(); -static bool test_exp(FILE *fp, BN_CTX *ctx); static bool test_mod_sqrt(FILE *fp, BN_CTX *ctx); static bool TestBN2BinPadded(BN_CTX *ctx); static bool TestDec2BN(BN_CTX *ctx); @@ -171,12 +170,6 @@ int main(int argc, char *argv[]) { "| grep -v 0 */\n"); puts_fp(bc_file.get(), "obase=16\nibase=16\n"); - message(bc_file.get(), "BN_exp"); - if (!test_exp(bc_file.get(), ctx.get())) { - return 1; - } - flush_fp(bc_file.get()); - message(bc_file.get(), "BN_mod_sqrt"); if (!test_mod_sqrt(bc_file.get(), ctx.get())) { return 1; @@ -650,6 +643,24 @@ static bool TestModExp(FileTest *t, BN_CTX *ctx) { return true; } +static bool TestExp(FileTest *t, BN_CTX *ctx) { + ScopedBIGNUM a = GetBIGNUM(t, "A"); + ScopedBIGNUM e = GetBIGNUM(t, "E"); + ScopedBIGNUM exp = GetBIGNUM(t, "Exp"); + if (!a || !e || !exp) { + return false; + } + + ScopedBIGNUM ret(BN_new()); + if (!ret || + !BN_exp(ret.get(), a.get(), e.get(), ctx) || + !ExpectBIGNUMsEqual(t, "A ^ E", exp.get(), ret.get())) { + return false; + } + + return true; +} + struct Test { const char *name; bool (*func)(FileTest *t, BN_CTX *ctx); @@ -665,6 +676,7 @@ static const Test kTests[] = { {"Quotient", TestQuotient}, {"ModMul", TestModMul}, {"ModExp", TestModExp}, + {"Exp", TestExp}, }; static bool RunTest(FileTest *t, void *arg) { @@ -686,50 +698,6 @@ static int rand_neg() { return sign[(neg++) % 8]; } -static bool test_exp(FILE *fp, BN_CTX *ctx) { - ScopedBIGNUM a(BN_new()); - ScopedBIGNUM b(BN_new()); - ScopedBIGNUM d(BN_new()); - ScopedBIGNUM e(BN_new()); - if (!a || !b || !d || !e) { - return false; - } - - for (int i = 0; i < num2; i++) { - if (!BN_rand(a.get(), 20 + i * 5, 0, 0) || - !BN_rand(b.get(), 2 + i, 0, 0) || - !BN_exp(d.get(), a.get(), b.get(), ctx)) { - return false; - } - - if (fp != NULL) { - BN_print_fp(fp, a.get()); - puts_fp(fp, " ^ "); - BN_print_fp(fp, b.get()); - puts_fp(fp, " - "); - BN_print_fp(fp, d.get()); - puts_fp(fp, "\n"); - } - if (!BN_one(e.get())) { - return false; - } - while (!BN_is_zero(b.get())) { - if (!BN_mul(e.get(), e.get(), a.get(), ctx) || - !BN_sub(b.get(), b.get(), BN_value_one())) { - return false; - } - } - if (!BN_sub(e.get(), e.get(), d.get())) { - return false; - } - if (!BN_is_zero(e.get())) { - fprintf(stderr, "Exponentiation test failed!\n"); - return false; - } - } - return true; -} - static bool test_mod_sqrt(FILE *fp, BN_CTX *ctx) { ScopedBIGNUM a(BN_new()); ScopedBIGNUM p(BN_new()); diff --git a/crypto/bn/bn_tests.txt b/crypto/bn/bn_tests.txt index b93e1f92f..48678bc6b 100644 --- a/crypto/bn/bn_tests.txt +++ b/crypto/bn/bn_tests.txt @@ -10178,3 +10178,28 @@ ModExp = ba16d7f3f6e162ce248490d164a13c00e7720d8a667e2d3ebeb13f1663e15ef5408d5b5 A = 9025e6183706105e948b1b0edf922f9011b9e11887d70adb00b26f272b9e76a38f3099084d9cccf12d04b1a99c0f654f8b9ed90c6dff9478c60bf05d58d734ab60eaefa14a22230ec60c90dc1f0704b61eef0bef345785ae0e6a9af7db069cf6bd2b4e0fe58a0ade83c7e46a04b9fe1d24cb9b65c6f80de713e61d70eae5b286 E = d7e6df5d755284929b986cd9b61c9c2c8843f24c711fbdbae1a468edcae159400943725570726cdc92b3ea94f9f206729516fdda83e31d815b0c7720e7598a91d992273e3bd8ac413b441d8f1dfe5aa7c3bf3ef573adc38292676217467731e6cf440a59611b8110af88d3e62f60209b513b01fbb69a097458ad02096b5e38f0 M = e4e784aa1fa88625a43ba0185a153a929663920be7fe674a4d33c943d3b898cff051482e7050a070cede53be5e89f31515772c7aea637576f99f82708f89d9e244f6ad3a24a02cbe5c0ff7bcf2dad5491f53db7c3f2698a7c41b44f086652f17bb05fe4c5c0a92433c34086b49d7e1825b28bab6c5a9bd0bc95b53d659afa0d7 + + +# Exp tests. +# +# These test vectors satisfy A ^ E = Exp. + +Exp = aa6d7ac431 +A = d0e07 +E = 2 + +Exp = 12d416b110dbb4e467ff0c89a22122f4da8240 +A = 1a18cf6 +E = 6 + +Exp = 49a3b33e23d84f1ce0d5d83f5dcb651d50cf3920f0143da2310d0512a90a06cd8f38977df8a756c30883de38df092000 +A = 2a3acbd2 +E = d + +Exp = 5b4a0d5a956f885f275712b194459980f24708bfb6393d71bd37dce852ce455724f5ee5030775fb86b4295edc98afaafc097e4d82a97c0078ec0eac763db16549c5145c4cf2d3124f88cf9a5c71da0625afb99b26801786fe49a778415dc025954021753d08691947a208b613f0be5c1 +A = 54b3ae461 +E = 1a + +Exp = a0ea5f6a4de49beb8fb7f0dab280d6a32c5a3814c9a5153a7944cec0a9028497846a8a89044348721a0bb5f0c3ded3e980574ea321b0cdb0ead4f4e93841ea7478a7f15d9729b646a8165813a0750e8124f5465dda9b105e1bbeff18fd09c09a2e26610d9176d253b877c3a8908a6be521cbe1e472a7a1b7820e4e890f8f28aacd34609c686e76e15b01bd9324a71290812724ea564d11c874a6765b262c3e57d479da0287a76026a1e8fe53da0b02405da1d379eaa30fc65f +A = fccec0f6df +E = 25 diff --git a/crypto/bn/check_bn_tests.go b/crypto/bn/check_bn_tests.go index d49c2b768..96411667c 100644 --- a/crypto/bn/check_bn_tests.go +++ b/crypto/bn/check_bn_tests.go @@ -222,6 +222,11 @@ func main() { r := new(big.Int).Exp(test.Values["A"], test.Values["E"], test.Values["M"]) checkResult(test, "A ^ E (mod M)", "ModExp", r) } + case "Exp": + if checkKeys(test, "A", "E", "Exp") { + r := new(big.Int).Exp(test.Values["A"], test.Values["E"], nil) + checkResult(test, "A ^ E", "Exp", r) + } default: fmt.Fprintf(os.Stderr, "Line %d: unknown test type %q.\n", test.LineNumber, test.Type) } From ce9a2166d6bc7aff8bd594312ec294fe8dbc690d Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sun, 3 Jul 2016 12:03:54 -0400 Subject: [PATCH 38/40] Document that BN_mod_sqrt assumes p is a prime. Change-Id: I5be2337ce6c333b704894c64e7931919bc047995 Reviewed-on: https://boringssl-review.googlesource.com/8595 Reviewed-by: David Benjamin --- crypto/bn/sqrt.c | 9 ++++----- include/openssl/bn.h | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crypto/bn/sqrt.c b/crypto/bn/sqrt.c index 2ed66c22c..e3a7b9ac6 100644 --- a/crypto/bn/sqrt.c +++ b/crypto/bn/sqrt.c @@ -57,12 +57,11 @@ #include -/* Returns 'ret' such that - * ret^2 == a (mod p), - * using the Tonelli/Shanks algorithm (cf. Henri Cohen, "A Course - * in Algebraic Computational Number Theory", algorithm 1.5.1). - * 'p' must be prime! */ BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) { + /* Compute a square root of |a| mod |p| using the Tonelli/Shanks algorithm + * (cf. Henri Cohen, "A Course in Algebraic Computational Number Theory", + * algorithm 1.5.1). |p| is assumed to be a prime. */ + BIGNUM *ret = in; int err = 1; int r; diff --git a/include/openssl/bn.h b/include/openssl/bn.h index 23dd8073f..a6866964d 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -565,7 +565,8 @@ OPENSSL_EXPORT int BN_mod_lshift1(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, OPENSSL_EXPORT int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *m); -/* BN_mod_sqrt returns a |BIGNUM|, r, such that r^2 == a (mod p). */ +/* BN_mod_sqrt returns a |BIGNUM|, r, such that r^2 == a (mod p). |p| must be a + * prime. */ OPENSSL_EXPORT BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); From 95c69563dc5422c3b3cd3a0bf435944a7530a12d Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Wed, 29 Jun 2016 18:15:03 -0400 Subject: [PATCH 39/40] Add version tolerance tests for DTLS. Also move them with the other version negotiation tests. Change-Id: I8ea5777c131f8ab618de3c6d02038e802bd34dd0 Reviewed-on: https://boringssl-review.googlesource.com/8550 Reviewed-by: David Benjamin --- ssl/test/runner/runner.go | 102 ++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index be88af6e3..7596485ec 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -1378,37 +1378,6 @@ func addBasicTests() { }, expectedVersion: VersionTLS13, }, - { - testType: serverTest, - name: "MinorVersionTolerance", - config: Config{ - Bugs: ProtocolBugs{ - SendClientVersion: 0x03ff, - }, - }, - expectedVersion: VersionTLS13, - }, - { - testType: serverTest, - name: "MajorVersionTolerance", - config: Config{ - Bugs: ProtocolBugs{ - SendClientVersion: 0x0400, - }, - }, - expectedVersion: VersionTLS13, - }, - { - testType: serverTest, - name: "VersionTooLow", - config: Config{ - Bugs: ProtocolBugs{ - SendClientVersion: 0x0200, - }, - }, - shouldFail: true, - expectedError: ":UNSUPPORTED_PROTOCOL:", - }, { testType: serverTest, name: "HttpGET", @@ -3668,6 +3637,77 @@ func addVersionNegotiationTests() { } } } + + // Test for version tolerance. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "MinorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x03ff, + }, + }, + expectedVersion: VersionTLS13, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "MajorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0400, + }, + }, + expectedVersion: VersionTLS13, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "MinorVersionTolerance-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x03ff, + }, + }, + expectedVersion: VersionTLS12, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "MajorVersionTolerance-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0400, + }, + }, + expectedVersion: VersionTLS12, + }) + + // Test that versions below 3.0 are rejected. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "VersionTooLow", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0200, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "VersionTooLow-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + // 0x0201 is the lowest version expressable in + // DTLS. + SendClientVersion: 0x0201, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) } func addMinimumVersionTests() { From ff26f09a059dc45337b3e20517960a86e2682165 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Fri, 1 Jul 2016 16:13:42 -0400 Subject: [PATCH 40/40] Fix c.in.decrypt error handling in runner. Part of this was we messed up the TLS 1.3 logic slightly, though the root bug is https://go-review.googlesource.com/#/c/24709/. Change-Id: I0a99b935f0e9a9c8edd5aa6cc56f3b2cb594703b Reviewed-on: https://boringssl-review.googlesource.com/8583 Reviewed-by: Adam Langley --- ssl/test/runner/conn.go | 11 ++++++----- ssl/test/runner/dtls.go | 12 ++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index 6c127e6ae..551c6bc20 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go @@ -765,17 +765,18 @@ func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) { // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) - ok, off, encTyp, err := c.in.decrypt(b) + ok, off, encTyp, alertValue := c.in.decrypt(b) + if !ok { + return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue)) + } + b.off = off + if c.vers >= VersionTLS13 && c.in.cipher != nil { if typ != recordTypeApplicationData { return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: outer record type is not application data")) } typ = encTyp } - if !ok { - c.in.setErrorLocked(c.sendAlert(err)) - } - b.off = off return typ, b, nil } diff --git a/ssl/test/runner/dtls.go b/ssl/test/runner/dtls.go index 06ec974c7..f4c5f71e9 100644 --- a/ssl/test/runner/dtls.go +++ b/ssl/test/runner/dtls.go @@ -111,14 +111,18 @@ func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) - // TODO(nharper): Once DTLS 1.3 is defined, handle the extra - // parameter from decrypt. - ok, off, _, err := c.in.decrypt(b) + ok, off, _, alertValue := c.in.decrypt(b) if !ok { - c.in.setErrorLocked(c.sendAlert(err)) + // A real DTLS implementation would silently ignore bad records, + // but we want to notice errors from the implementation under + // test. + return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue)) } b.off = off + // TODO(nharper): Once DTLS 1.3 is defined, handle the extra + // parameter from decrypt. + // Require that ChangeCipherSpec always share a packet with either the // previous or next handshake message. if newPacket && typ == recordTypeChangeCipherSpec && c.rawInput == nil {