diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a9a0c270..b2d35eb4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -165,7 +165,7 @@ jobs: - run: | sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes if [[ "${{ matrix.target }}" == *port* ]]; then source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf; cat test/mosquitto.conf; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 ; fi - make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }} MULTIREC=NO + make -C test ${{ matrix.target }} IPV6=${{ matrix.ipv6 }} SSL=${{ matrix.ssl }} miprtxbe: runs-on: ubuntu-latest @@ -180,7 +180,7 @@ jobs: - run: | sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes source ./test/setup_ga_network.sh ; export ENV="-e Tmp=. -e WINEDEBUG=-all -e HOST_IP -e HOST_IPV6 -e HOST_IFC -e RTX" ; sed -i -e "s/127.0.0.1//" test/mosquitto.conf ; ./test/setup_mqtt_server.sh ; make -C test port_tap_bridge ; test/port_tap_bridge & sleep 2 - make -C test mip_port_s390 MULTIREC=NO + make -C test mip_port_s390 l2: runs-on: ubuntu-latest @@ -354,7 +354,7 @@ jobs: steps: - uses: actions/checkout@v5 with: { fetch-depth: 2 } - - run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test s390 SSL=${{ matrix.ssl }} MULTIREC=NO + - run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test s390 SSL=${{ matrix.ssl }} armhf: runs-on: ubuntu-latest strategy: @@ -367,7 +367,7 @@ jobs: steps: - uses: actions/checkout@v5 with: { fetch-depth: 2 } - - run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test armhf SSL=${{ matrix.ssl }} MULTIREC=NO + - run: sudo apt -y update ; sudo apt -y install binfmt-support qemu-user-static && ./test/setup_mqtt_server.sh && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make -C test armhf SSL=${{ matrix.ssl }} unamalgamated-mg_prefix: runs-on: ubuntu-latest strategy: diff --git a/mongoose.c b/mongoose.c index 51f7f38c..6f71df5a 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2744,9 +2744,8 @@ bool mg_ota_flash_end(struct mg_flash *flash) { uint8_t hash[32]; size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic mg_sha256(hash, (uint8_t *) base, fw_size); - ok = mg_uecc_verify(s_pubkey, hash, sizeof(hash), - (uint8_t *) base + fw_size, - mg_uecc_secp256r1()) == 1; + ok = mg_p256_verify(s_pubkey, hash, sizeof(hash), + (uint8_t *) base + fw_size) == 1; MG_INFO(("Signature: %s", ok ? "ok" : "fail")); } else { ok = false; @@ -5527,39 +5526,14 @@ size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts, } #if MG_TLS == MG_TLS_BUILTIN -typedef struct { - MG_UECC_HashContext uECC; - mg_sha256_ctx sha256; -} jwt_hash_ctx; - -static void jwt_hash_init(const MG_UECC_HashContext *ctx) { - jwt_hash_ctx *c = (jwt_hash_ctx *) ctx; - mg_sha256_init(&c->sha256); -} - -static void jwt_hash_update(const MG_UECC_HashContext *ctx, const uint8_t *msg, - unsigned len) { - jwt_hash_ctx *c = (jwt_hash_ctx *) ctx; - mg_sha256_update(&c->sha256, msg, len); -} - -static void jwt_hash_finish(const MG_UECC_HashContext *ctx, uint8_t *hash) { - jwt_hash_ctx *c = (jwt_hash_ctx *) ctx; - mg_sha256_final(hash, &c->sha256); -} - size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf, size_t len) { - uint8_t hash[32], sig[64], tmp[2 * 32 + 64]; - jwt_hash_ctx ctx = { - {jwt_hash_init, jwt_hash_update, jwt_hash_finish, 64, 32, tmp}, - {{0}, 0, 0, {0}}}; + uint8_t hash[32], sig[64]; size_t n = jwt_header_payload("ES256", opts, buf, len); size_t m; if (n == 0 || n + 1 >= len) return 0; mg_sha256(hash, (uint8_t *) buf, n); - if (mg_uecc_sign_deterministic(opts->private_key, hash, sizeof(hash), - &ctx.uECC, sig, mg_uecc_secp256r1()) != 1) + if (mg_p256_sign(opts->private_key, hash, sizeof(hash), sig) != 1) return 0; buf[n++] = '.'; m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n); @@ -5575,8 +5549,7 @@ size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts, if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64) return 0; mg_sha256(hash, (uint8_t *) jwt.buf, n); - if (mg_uecc_verify(opts->public_key, hash, sizeof(hash), sig, - mg_uecc_secp256r1()) != 1) + if (mg_p256_verify(opts->public_key, hash, sizeof(hash), sig) != 1) return 0; return mg_base64url_decode(p.buf, p.len, buf, len); } @@ -7320,9 +7293,13 @@ void mg_mdash_init(struct mg_mgr *mgr) { } void mg_mdash_poll(struct mg_mgr *mgr) { +#if MG_TLS == MG_TLS_NONE + (void) mgr, (void) mg_mdash_fn; // mdash needs wss://, nothing to do +#else static uint64_t timer = 0; - // No RPC handlers means mdash was never set up, or mg_mdash_free() ran - if (mg_mdash_conn == NULL && mgr->rpcs != NULL && + // No RPC handlers means mdash was never set up, or mg_mdash_free() ran. + // No key means mdash would reject us, so do not connect at all + if (mg_mdash_conn == NULL && mgr->rpcs != NULL && MG_MDASH_KEY[0] != '\0' && mg_timer_expired(&timer, 1000, mg_millis())) { MG_INFO(("Connecting to %s", MG_MDASH_URL)); mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL, @@ -7330,6 +7307,7 @@ void mg_mdash_poll(struct mg_mgr *mgr) { "Authorization: Bearer %s\r\n", MG_MDASH_URL, MG_MDASH_KEY); } +#endif } void mg_mdash_free(struct mg_mgr *mgr) { @@ -17130,6 +17108,7 @@ enum mg_tls_hs_state { // Server state machine: MG_TLS_STATE_SERVER_START, // Wait for ClientHello + MG_TLS_STATE_SERVER_RETRY, // Wait for ClientHello after HelloRetry MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish @@ -17175,9 +17154,9 @@ struct tls_data { uint8_t random[32]; // client random from ClientHello uint8_t session_id[32]; // client session ID between the handshake states - uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t p256_cli[32]; // client P-256 key between the handshake states - uint8_t x25519_sec[32]; // ECDHE secret (X25519 or P-256) between the states + uint8_t p256_peer[64]; // server: client P-256 public key X || Y + uint8_t ecdhe_sec[32]; // P-256 ECDHE shared secret between the states bool skip_verification; // do not perform checks on server certificate bool cert_requested; // client received a CertificateRequest @@ -17201,6 +17180,8 @@ struct tls_data { size_t pubkeysz; // size of the server public key uint8_t sighash[32]; // calculated signature verification hash uint8_t sighash384[48]; // same, SHA-384, for ecdsa_secp384r1_sha384 + uint64_t hs_start; // mg_millis() when the handshake started + uint64_t hs_proc; // ms spent processing handshake messages struct tls_enc enc; // actual keys in use at this time struct tls_enc app_keys; // storage during two-way auth handshake @@ -17392,7 +17373,7 @@ static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz, memmove(hash, secret, hashsz); } -// at this point we have x25519 shared secret, we can generate a set of derived +// at this point we have ECDHE shared secret, we can generate a set of derived // handshake encryption keys static void mg_tls_generate_handshake_keys(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; @@ -17413,12 +17394,11 @@ static void mg_tls_generate_handshake_keys(struct mg_connection *c) { mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, 32, pre_extract_secret, 32); mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret, - sizeof(pre_extract_secret), tls->x25519_sec, - sizeof(tls->x25519_sec)); + sizeof(pre_extract_secret), tls->ecdhe_sec, + sizeof(tls->ecdhe_sec)); // ECDHE keys and secret are not needed anymore, wipe for forward secrecy - mg_bzero(tls->x25519_cli, sizeof(tls->x25519_cli)); mg_bzero(tls->p256_cli, sizeof(tls->p256_cli)); - mg_bzero(tls->x25519_sec, sizeof(tls->x25519_sec)); + mg_bzero(tls->ecdhe_sec, sizeof(tls->ecdhe_sec)); mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32); // mg_sha256_final is not idempotent, need to copy sha256 context to calculate @@ -17581,19 +17561,20 @@ static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, static void verbose_alert(uint8_t *buf) { uint8_t level = buf[0], desc = buf[1]; - MG_INFO(("TLS ALERT received: level=%d, desc=%d (%s)", level, desc, - desc == 0 ? "close_notify" - : desc == 10 ? "unexpected_message" - : desc == 20 ? "bad_record_mac" - : desc == 21 ? "decryption_failed" - : desc == 40 ? "handshake_failure" - : desc == 42 ? "bad_certificate" - : desc == 43 ? "unsupported_certificate" - : desc == 44 ? "certificate_revoked" - : desc == 45 ? "certificate_expired" - : desc == 46 ? "certificate_unknown" - : desc == 48 ? "unknown_ca" - : "unknown")); + MG_LOG(desc == 0 ? MG_LL_VERBOSE : MG_LL_INFO, // close_notify is normal + ("TLS ALERT received: level=%d, desc=%d (%s)", level, desc, + desc == 0 ? "close_notify" + : desc == 10 ? "unexpected_message" + : desc == 20 ? "bad_record_mac" + : desc == 21 ? "decryption_failed" + : desc == 40 ? "handshake_failure" + : desc == 42 ? "bad_certificate" + : desc == 43 ? "unsupported_certificate" + : desc == 44 ? "certificate_revoked" + : desc == 45 ? "certificate_expired" + : desc == 46 ? "certificate_unknown" + : desc == 48 ? "unknown_ca" + : "unknown")); } // read an encrypted record, decrypt it in place @@ -17728,7 +17709,8 @@ static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, if (hash384 != NULL) mg_sha384(hash384, sig_content, sizeof(sig_content)); } -// read and parse ClientHello record +// read and parse ClientHello record. Return 0 if it has a P-256 key share, +// 1 if it does not and HelloRetryRequest must be sent, negative otherwise static int mg_tls_server_recv_hello(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; struct mg_iobuf *rio = &c->rtls; @@ -17738,9 +17720,13 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) { uint16_t ext_len; uint8_t *ext; uint16_t msgsz; + bool have_p256 = false; + bool is_retry = tls->state == MG_TLS_STATE_SERVER_RETRY; - if (!mg_tls_got_record(c)) { - return MG_IO_WAIT; + for (;;) { + if (!mg_tls_got_record(c)) return MG_IO_WAIT; + if (!is_retry || rio->buf[0] != MG_TLS_CHANGE_CIPHER) break; + mg_tls_drop_record(c); // compatibility mode CCS before 2nd ClientHello } if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) { mg_error(c, "not a client hello packet"); @@ -17784,16 +17770,21 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) { goto fail; for (k = 0; k < key_exchange_len;) { uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); + uint16_t group = MG_LOAD_BE16(key_exchange + k); if (((uint32_t) m + k + 4) > key_exchange_len) goto fail; - if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) { - memmove(tls->x25519_cli, key_exchange + k + 4, m); - mg_tls_drop_record(c); - return 0; + // secp256r1, uncompressed point 0x04 || X || Y + if (group == 0x0017 && m == 65 && key_exchange[k + 4] == 0x04) { + memmove(tls->p256_peer, key_exchange + k + 5, 64); + have_p256 = true; } k += (uint16_t) (m + 4); } j += (uint16_t) (n + 4); } + if (have_p256 || !is_retry) { // No P-256 share: ask for it, but only once + mg_tls_drop_record(c); + return have_p256 ? 0 : 1; + } fail: mg_error(c, "bad client hello"); return -1; @@ -17807,58 +17798,98 @@ fail: static bool mg_tls_server_send_hello(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; struct mg_iobuf *wio = &tls->send; + uint8_t hello[155], pub[65], prv[32], hdr[5] = {0x16, 0x03, 0x03, 0, 0}; + size_t n = sizeof(hello); // ServerHello length, including 4-byte header - // clang-format off - uint8_t msg_server_hello[122] = { - // server hello, tls 1.2 - 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, - // random (32 bytes) - PLACEHOLDER_32B, - // session ID length + session ID (32 bytes) - 0x20, PLACEHOLDER_32B, -#if MG_ENABLE_CHACHA20 - // TLS_CHACHA20_POLY1305_SHA256 + no compression - 0x13, 0x03, 0x00, -#else - // TLS_AES_128_GCM_SHA256 + no compression - 0x13, 0x01, 0x00, -#endif - // extensions + keyshare - 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, - // x25519 keyshare - PLACEHOLDER_32B, - // supported versions (tls1.3 == 0x304) - 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}; - // clang-format on - - // calculate keyshare - uint8_t x25519_pub[X25519_BYTES]; - uint8_t x25519_prv[X25519_BYTES]; - if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG"); - if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) { + // calculate P-256 keyshare and the shared secret + pub[0] = 0x04; // uncompressed point 0x04 || X || Y + if (!mg_p256_make_key(pub + 1, prv) || + !mg_p256_shared_secret(tls->p256_peer, prv, tls->ecdhe_sec)) { + mg_bzero(prv, sizeof(prv)); mg_error(c, "bad key"); return false; } - mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); + mg_bzero(prv, sizeof(prv)); + mg_tls_hexdump("s ecdhe sec", tls->ecdhe_sec, sizeof(tls->ecdhe_sec)); - // fill in the gaps: random + session ID + keyshare - memmove(msg_server_hello + 6, tls->random, sizeof(tls->random)); - memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id)); - memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub)); + hello[0] = 0x02; // server hello, tls 1.2 + MG_STORE_BE24(hello + 1, n - 4); + hello[4] = 0x03, hello[5] = 0x03; + if (!mg_random(hello + 6, 32)) { // server random, RFC 8446 4.1.3 + mg_error(c, "RNG"); + return false; + } + hello[38] = 0x20; // session ID length + session ID (32 bytes) + memmove(hello + 39, tls->session_id, sizeof(tls->session_id)); +#if MG_ENABLE_CHACHA20 + hello[71] = 0x13, hello[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256 +#else + hello[71] = 0x13, hello[72] = 0x01; // TLS_AES_128_GCM_SHA256 +#endif + hello[73] = 0x00; // no compression + MG_STORE_BE16(hello + 74, 65 + 14); // extensions length + MG_STORE_BE16(hello + 76, 0x0033); // key share + MG_STORE_BE16(hello + 78, 65 + 4); + MG_STORE_BE16(hello + 80, 0x0017); // secp256r1 + MG_STORE_BE16(hello + 82, 65); + memmove(hello + 84, pub, sizeof(pub)); + memmove(hello + 149, "\x00\x2b\x00\x02\x03\x04", 6); // tls1.3 == 0x304 + MG_STORE_BE16(hdr + 3, n); // server hello message - if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 || - mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) == - 0) + if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 || + mg_iobuf_add(wio, wio->len, hello, n) == 0) return false; - mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello)); + mg_sha256_update(&tls->sha256, hello, n); - // change cipher message - if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0) + // change cipher message, unless it was already sent after HelloRetryRequest + if (tls->state != MG_TLS_STATE_SERVER_RETRY && + mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0) return false; return true; } +// HelloRetryRequest random, SHA-256 of "HelloRetryRequest", RFC 8446 4.1.3 +static const uint8_t hrr_random[32] = { + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, + 0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, + 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c}; + +// put HelloRetryRequest record into wio buffer, asking for a P-256 key share. +// Replace ClientHello in the transcript with message_hash, RFC 8446 4.4.1 +static bool mg_tls_server_send_hrr(struct mg_connection *c) { + struct tls_data *tls = (struct tls_data *) c->tls; + struct mg_iobuf *wio = &tls->send; + uint8_t hrr[88], hdr[5] = {0x16, 0x03, 0x03, 0, sizeof(hrr)}; + uint8_t mh[4 + 32] = {0xfe, 0, 0, 32}; // message_hash(ClientHello1) + + hrr[0] = 0x02; // server hello, tls 1.2 + MG_STORE_BE24(hrr + 1, sizeof(hrr) - 4); + hrr[4] = 0x03, hrr[5] = 0x03; + memmove(hrr + 6, hrr_random, sizeof(hrr_random)); + hrr[38] = 0x20; // session ID length + session ID (32 bytes) + memmove(hrr + 39, tls->session_id, sizeof(tls->session_id)); +#if MG_ENABLE_CHACHA20 + hrr[71] = 0x13, hrr[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256 +#else + hrr[71] = 0x13, hrr[72] = 0x01; // TLS_AES_128_GCM_SHA256 +#endif + hrr[73] = 0x00; // no compression + // extensions: supported versions tls1.3, key share secp256r1 (group only) + memmove(hrr + 74, "\x00\x0c\x00\x2b\x00\x02\x03\x04\x00\x33\x00\x02\x00\x17", + 14); + + mg_sha256_final(mh + 4, &tls->sha256); + mg_sha256_init(&tls->sha256); + mg_sha256_update(&tls->sha256, mh, sizeof(mh)); + mg_sha256_update(&tls->sha256, hrr, sizeof(hrr)); + + // HelloRetryRequest message, followed by change cipher message + return mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) != 0 && + mg_iobuf_add(wio, wio->len, hrr, sizeof(hrr)) != 0 && + mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) != 0; +} + static bool mg_tls_server_send_ext(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; // server extensions @@ -17868,14 +17899,10 @@ static bool mg_tls_server_send_ext(struct mg_connection *c) { } // signature algorithms we actually support: ecdsa_secp256r1_sha256, -// ecdsa_secp384r1_sha384 (if enabled), rsa_pss_rsae_sha256, rsa_pkcs1_sha256 +// ecdsa_secp384r1_sha384, rsa_pss_rsae_sha256, rsa_pkcs1_sha256 static const uint8_t secp256r1_sig_algs[] = { -#if MG_UECC_SUPPORTS_secp384r1 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01}; -#else - 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01}; -#endif static bool mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; @@ -17947,28 +17974,6 @@ static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) { return res; } -// type adapter between uECC hash context and our sha256 implementation -typedef struct SHA256_HashContext { - MG_UECC_HashContext uECC; - mg_sha256_ctx ctx; -} SHA256_HashContext; - -static void init_SHA256(const MG_UECC_HashContext *base) { - SHA256_HashContext *c = (SHA256_HashContext *) base; - mg_sha256_init(&c->ctx); -} - -static void update_SHA256(const MG_UECC_HashContext *base, - const uint8_t *message, unsigned message_size) { - SHA256_HashContext *c = (SHA256_HashContext *) base; - mg_sha256_update(&c->ctx, message, message_size); -} -static void finish_SHA256(const MG_UECC_HashContext *base, - uint8_t *hash_result) { - SHA256_HashContext *c = (SHA256_HashContext *) base; - mg_sha256_final(hash_result, &c->ctx); -} - static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed, size_t seed_len) { uint32_t counter = 0; @@ -18211,15 +18216,13 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { } else { // EC certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; - uint8_t tmp[2 * 32 + 64] = {0}; - struct SHA256_HashContext ctx = { - {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, - {{0}, 0, 0, {0}}}; size_t sigsz, verifysz = 0; int neg1, neg2; uint8_t sig[64] = {0}; - mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig, - mg_uecc_secp256r1()); + if (!mg_p256_sign(tls->ec_key, hash, sizeof(hash), sig)) { + mg_error(c, "P-256 sign"); + return false; + } neg1 = !!(sig[0] & 0x80); neg2 = !!(sig[32] & 0x80); @@ -18282,7 +18285,6 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; struct mg_iobuf *wio = &tls->send; - uint8_t x25519_pub[X25519_BYTES]; uint8_t p256_pub[64]; // X || Y, sent as an uncompressed point 0x04 || X || Y // - "signature algorithms we actually support", see above @@ -18296,11 +18298,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { 0xfe, 0x00, 0x00, 0xfe}; // clang-format off - uint8_t msg_client_hello[216] = { + uint8_t msg_client_hello[178] = { // TLS Client Hello header reported as TLS1.2 (5) - 0x16, 0x03, 0x03, 0x00, 0xfe, + 0x16, 0x03, 0x03, 0x00, 0xd8, // client hello, tls 1.2 (6) - 0x01, 0x00, 0x00, 0x8c, 0x03, 0x03, + 0x01, 0x00, 0x00, 0xd4, 0x03, 0x03, // random (32 bytes) PLACEHOLDER_32B, // session ID length + session ID (32 bytes) @@ -18317,17 +18319,15 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { 0x01, 0x00, // extensions + keyshare 0x00, 0xfe, - // keyshare: x25519 - 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, 0x20, - PLACEHOLDER_32B, // keyshare: secp256r1, uncompressed point + 0x00, 0x33, 0x00, 0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04, PLACEHOLDER_32B, PLACEHOLDER_32B, - // supported groups (x25519, secp256r1) - 0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x17, + // supported groups (secp256r1) + 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x17, // supported versions (tls1.3 == 0x304) 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, // session ticket (none) - 0x00, 0x23, 0x00, 0x00, // 215 bytes till here + 0x00, 0x23, 0x00, 0x00, // 177 bytes till here }; // clang-format on const char *hostname = tls->hostname; @@ -18340,11 +18340,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { // patch ClientHello with correct hostname ext length (if any) MG_STORE_BE16(msg_client_hello + 3, - hostname_extsz + 254 - 9 - 34 + sig_alg_sz); + hostname_extsz + 216 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 7, - hostname_extsz + 250 - 9 - 34 + sig_alg_sz); + hostname_extsz + 212 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 82, - hostname_extsz + 175 - 9 - 34 + sig_alg_sz); + hostname_extsz + 137 - 9 - 34 + sig_alg_sz); if (hostnamesz > 0) { MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5); @@ -18352,21 +18352,18 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { MG_STORE_BE16(server_name_ext + 7, hostnamesz); } - // calculate keyshares - if (!mg_random(tls->x25519_cli, sizeof(tls->x25519_cli))) mg_error(c, "RNG"); - mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1); - if (!mg_uecc_make_key(p256_pub, tls->p256_cli, mg_uecc_secp256r1())) { + // calculate keyshare + if (!mg_p256_make_key(p256_pub, tls->p256_cli)) { mg_error(c, "P-256 key"); return false; // Do not send uninitialised p256_pub } - // fill in the gaps: random + session ID + keyshares + // fill in the gaps: random + session ID + keyshare if (!mg_random(tls->session_id, sizeof(tls->session_id))) mg_error(c, "RNG"); if (!mg_random(tls->random, sizeof(tls->random))) mg_error(c, "RNG"); memmove(msg_client_hello + 11, tls->random, sizeof(tls->random)); memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id)); - memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub)); - memmove(msg_client_hello + 131, p256_pub, sizeof(p256_pub)); + memmove(msg_client_hello + 95, p256_pub, sizeof(p256_pub)); // client hello message if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) == @@ -18439,26 +18436,17 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) { key_exchange_len = MG_LOAD_BE16(ext + j + 6); key_exchange = ext + j + 8; if (key_exchange_len > ext_len2 - 4) goto fail; - if (group == 0x001d) { // x25519 - if (key_exchange_len != 32 || - mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) < - 0) { - mg_error(c, "bad key"); - return -1; - } - } else if (group == 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y - if (key_exchange_len != 65 || key_exchange[0] != 0x04 || - !mg_uecc_valid_public_key(key_exchange + 1, mg_uecc_secp256r1()) || - !mg_uecc_shared_secret(key_exchange + 1, tls->p256_cli, - tls->x25519_sec, mg_uecc_secp256r1())) { - mg_error(c, "bad key"); - return -1; - } - } else { + if (group != 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y mg_error(c, "bad key exchange group"); return -1; } - // mg_tls_hexdump("c ecdhe sec", tls->x25519_sec, 32); + if (key_exchange_len != 65 || key_exchange[0] != 0x04 || + !mg_p256_shared_secret(key_exchange + 1, tls->p256_cli, + tls->ecdhe_sec)) { // Validates the point + mg_error(c, "bad key"); + return -1; + } + // mg_tls_hexdump("c ecdhe sec", tls->ecdhe_sec, 32); mg_tls_drop_record(c); /* generate handshake keys */ mg_tls_generate_handshake_keys(c); @@ -18783,7 +18771,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, MG_ERROR(("cert verification error")); return 0; } -#if MG_UECC_SUPPORTS_secp256r1 if (issuer->pubkey.len == 64) { const uint32_t N = 32; if (a.len > N) a.value += (a.len - N), a.len = N; // padding @@ -18792,13 +18779,9 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, memmove(sig + (N - a.len), a.value, a.len); memset(sig + N, 0, N - b.len); memmove(sig + N + (N - b.len), b.value, b.len); - return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, - (unsigned) cert->tbshashsz, sig, - mg_uecc_secp256r1()); - } else -#endif -#if MG_UECC_SUPPORTS_secp384r1 - if (issuer->pubkey.len == 96) { + return mg_p256_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, + cert->tbshashsz, sig); + } else if (issuer->pubkey.len == 96) { const uint32_t N = 48; if (a.len > N) a.value += (a.len - N), a.len = N; // padding if (b.len > N) b.value += (b.len - N), b.len = N; @@ -18806,12 +18789,9 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, memmove(sig + (N - a.len), a.value, a.len); memset(sig + N, 0, N - b.len); memmove(sig + N + (N - b.len), b.value, b.len); - return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, - (unsigned) cert->tbshashsz, sig, - mg_uecc_secp384r1()); - } else -#endif - { + return mg_p384_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, + cert->tbshashsz, sig); + } else { MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len)); return 0; } @@ -19109,8 +19089,7 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) { return -1; } MG_VERBOSE(("certificate verification successful (RSA)")); - } else if (sigalg == 0x0403 || - (sigalg == 0x0503 && MG_UECC_SUPPORTS_secp384r1)) { + } else if (sigalg == 0x0403 || sigalg == 0x0503) { // ecdsa_secp256r1_sha256 or ecdsa_secp384r1_sha384. Extract certificate // signature and verify it using pubkey and sighash bool is384 = sigalg == 0x0503; @@ -19143,15 +19122,12 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) { memmove(sig + (N - r.len), r.value, r.len); memmove(sig + N + (N - s.len), s.value, s.len); -#if MG_UECC_SUPPORTS_secp384r1 if (is384) { - ok = mg_uecc_verify(tls->pubkey, tls->sighash384, - sizeof(tls->sighash384), sig, mg_uecc_secp384r1()); - } else -#endif - { - ok = mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), - sig, mg_uecc_secp256r1()); + ok = mg_p384_verify(tls->pubkey, tls->sighash384, + sizeof(tls->sighash384), sig); + } else { + ok = mg_p256_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), + sig); } if (ok != 1) { mg_error(c, "failed to verify EC certificate (certverify)"); @@ -19255,16 +19231,23 @@ static bool mg_tls_client_handshake(struct mg_connection *c) { static bool mg_tls_server_handshake(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; + int r; switch (tls->state) { case MG_TLS_STATE_SERVER_START: - if (mg_tls_server_recv_hello(c) < 0) break; + case MG_TLS_STATE_SERVER_RETRY: + if ((r = mg_tls_server_recv_hello(c)) < 0) break; + if (r == 1) { // No P-256 key share, ask for it + if (!mg_tls_server_send_hrr(c)) return false; + tls->state = MG_TLS_STATE_SERVER_RETRY; + break; + } if (!mg_tls_server_send_hello(c)) return false; mg_tls_generate_handshake_keys(c); if (!mg_tls_server_send_ext(c)) return false; if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false; - if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || - !mg_tls_server_send_finish(c)) - return false; + if (!mg_tls_send_cert(c, false)) return false; + if (!mg_tls_send_cert_verify(c, false)) return false; + if (!mg_tls_server_send_finish(c)) return false; if (tls->is_twoway) { // generate application keys at this point, keep using handshake keys struct tls_enc hs_keys = tls->enc; @@ -19305,14 +19288,25 @@ void mg_tls_handshake(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; long n; bool res; + uint64_t t; if (c->is_closing) return; // we don't clear rx buf, so ignore what's left if (tls->is_sntp_pending) return; + t = mg_millis(); if (c->is_client) { // will clear is_hs when sending last chunk res = mg_tls_client_handshake(c); } else { res = mg_tls_server_handshake(c); } + tls->hs_proc += mg_millis() - t; + if (res && !c->is_tls_hs) { + // CPU: key exchange, signing, certificate verification. Wait: the rest of + // the total, waiting for the peer and the network + unsigned long total = (unsigned long) (mg_millis() - tls->hs_start); + unsigned long cpu = (unsigned long) tls->hs_proc; + MG_DEBUG(("%lu TLS handshake done in %lu ms: %lu ms CPU, %lu ms peer wait", + c->id, total, cpu, total > cpu ? total - cpu : 0)); + } if (!res) { mg_error(c, "TLS OOM"); return; @@ -19731,7 +19725,7 @@ static int mg_parse_pem_certs(const struct mg_str pem, struct mg_str **ders) { return count; } -size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) { +size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) { struct mg_str der = mg_str_n(NULL, 0); size_t n = 0; if (buf == NULL || len < 32) return 0; @@ -19771,6 +19765,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { mg_error(c, "tls oom"); return; } + tls->hs_start = mg_millis(); tls->state = c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START; @@ -19956,11 +19951,15 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { long n = MG_IO_WAIT; bool was_throttled = c->is_tls_throttled; // see #3074 if (!was_throttled) { // encrypt new data + size_t i, rs = MG_TLS_RECORD_SIZE > 16384 ? 16384 : MG_TLS_RECORD_SIZE; if (len > MG_IO_SIZE) len = MG_IO_SIZE; - if (len > 16384) len = 16384; - if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA)) - return 0; // returning 0 means an OOM condition (iobuf couldn't resize), - // yet this is so far recoverable, let the caller decide + for (i = 0; i < len; i += rs) { // Split into records, send them at once + size_t m = len - i < rs ? len - i : rs; + if (!mg_tls_encrypt(c, (const uint8_t *) buf + i, m, MG_TLS_APP_DATA)) + return 0; // returning 0 means an OOM condition (iobuf couldn't + // resize), yet this is so far recoverable, let the caller + // decide + } } // else, resend outstanding encrypted data in tls->send while (tls->send.len > 0 && (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { @@ -22087,6 +22086,5666 @@ void mg_tls_ctx_free(struct mg_mgr *mgr) { } #endif +#ifdef MG_ENABLE_LINES +#line 1 "src/tls_p256.c" +#endif +// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by +// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN) +// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA +// code is always compiled, it is also used by P-384, see tls_p384.c +// +// Copyright (c) 2016 Thomas Pornin +// Copyright (c) 2017 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the +// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not +// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is +// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and +// public key directly + + + + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + +// From BearSSL src/codec/ccopy.c +void +mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = (unsigned char *)dst; + s = (const unsigned char *)src; + while (len -- > 0) { + uint32_t x, y; + + x = *s ++; + y = *d; + *d = (unsigned char)MG_BR_MUX(ctl, x, y); + d ++; + } +} + +// From BearSSL src/int/i32_div32.c +uint32_t +mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r) +{ + /* TODO: optimize this */ + uint32_t q; + uint32_t ch, cf; + int k; + + q = 0; + ch = MG_BR_EQ(hi, d); + hi = MG_BR_MUX(ch, 0, hi); + for (k = 31; k > 0; k --) { + int j; + uint32_t w, ctl, hi2, lo2; + + j = 32 - k; + w = (hi << j) | (lo >> k); + ctl = MG_BR_GE(w, d) | (hi >> k); + hi2 = (w - d) >> j; + lo2 = lo - (d << k); + hi = MG_BR_MUX(ctl, hi2, hi); + lo = MG_BR_MUX(ctl, lo2, lo); + q |= ctl << k; + } + cf = MG_BR_GE(lo, d) | hi; + q |= cf; + *r = MG_BR_MUX(cf, lo - d, lo); + return q; +} + +// From BearSSL src/int/i31_add.c +uint32_t +mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 63) >> 5; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = b[u]; + naw = aw + bw + cc; + cc = naw >> 31; + a[u] = MG_BR_MUX(ctl, naw & (uint32_t)0x7FFFFFFF, aw); + } + return cc; +} + +// From BearSSL src/int/i31_bitlen.c +uint32_t +mg_br_i31_bit_length(uint32_t *x, size_t xlen) +{ + uint32_t tw, twk; + + tw = 0; + twk = 0; + while (xlen -- > 0) { + uint32_t w, c; + + c = MG_BR_EQ(tw, 0); + w = x[xlen]; + tw = MG_BR_MUX(c, w, tw); + twk = MG_BR_MUX(c, (uint32_t)xlen, twk); + } + return (twk << 5) + MG_BR_BIT_LENGTH(tw); +} + +// From BearSSL src/int/i31_decmod.c +uint32_t +mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len, const uint32_t *m) +{ + /* + * Two-pass algorithm: in the first pass, we determine whether the + * value fits; in the second pass, we do the actual write. + * + * During the first pass, 'r' contains the comparison result so + * far: + * 0x00000000 value is equal to the modulus + * 0x00000001 value is greater than the modulus + * 0xFFFFFFFF value is lower than the modulus + * + * Since we iterate starting with the least significant bytes (at + * the end of src[]), each new comparison overrides the previous + * except when the comparison yields 0 (equal). + * + * During the second pass, 'r' is either 0xFFFFFFFF (value fits) + * or 0x00000000 (value does not fit). + * + * We must iterate over all bytes of the source, _and_ possibly + * some extra virtual bytes (with value 0) so as to cover the + * complete modulus as well. We also add 4 such extra bytes beyond + * the modulus length because it then guarantees that no accumulated + * partial word remains to be processed. + */ + const unsigned char *buf; + size_t mlen, tlen; + int pass; + uint32_t r; + + buf = (const unsigned char *)src; + mlen = (m[0] + 31) >> 5; + tlen = (mlen << 2); + if (tlen < len) { + tlen = len; + } + tlen += 4; + r = 0; + for (pass = 0; pass < 2; pass ++) { + size_t u, v; + uint32_t acc; + int acc_len; + + v = 1; + acc = 0; + acc_len = 0; + for (u = 0; u < tlen; u ++) { + uint32_t b; + + if (u < len) { + b = buf[len - 1 - u]; + } else { + b = 0; + } + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 31) { + uint32_t xw; + + xw = acc & (uint32_t)0x7FFFFFFF; + acc_len -= 31; + acc = b >> (8 - acc_len); + if (v <= mlen) { + if (pass) { + x[v] = r & xw; + } else { + uint32_t cc; + + cc = (uint32_t)MG_BR_CMP(xw, m[v]); + r = MG_BR_MUX(MG_BR_EQ(cc, 0), r, cc); + } + } else { + if (!pass) { + r = MG_BR_MUX(MG_BR_EQ(xw, 0), r, 1); + } + } + v ++; + } + } + + /* + * When we reach this point at the end of the first pass: + * r is either 0, 1 or -1; we want to set r to 0 if it + * is equal to 0 or 1, and leave it to -1 otherwise. + * + * When we reach this point at the end of the second pass: + * r is either 0 or -1; we want to leave that value + * untouched. This is a subcase of the previous. + */ + r >>= 1; + r |= (r << 1); + } + + x[0] = m[0]; + return r & (uint32_t)1; +} + +// From BearSSL src/int/i31_decode.c +void +mg_br_i31_decode(uint32_t *x, const void *src, size_t len) +{ + const unsigned char *buf; + size_t u, v; + uint32_t acc; + int acc_len; + + buf = (const unsigned char *)src; + u = len; + v = 1; + acc = 0; + acc_len = 0; + while (u -- > 0) { + uint32_t b; + + b = buf[u]; + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 31) { + x[v ++] = acc & (uint32_t)0x7FFFFFFF; + acc_len -= 31; + acc = b >> (8 - acc_len); + } + } + if (acc_len != 0) { + x[v ++] = acc; + } + x[0] = mg_br_i31_bit_length(x + 1, v - 1); +} + +// From BearSSL src/int/i31_encode.c +void +mg_br_i31_encode(void *dst, size_t len, const uint32_t *x) +{ + unsigned char *buf; + size_t k, xlen; + uint32_t acc; + int acc_len; + + xlen = (x[0] + 31) >> 5; + if (xlen == 0) { + memset(dst, 0, len); + return; + } + buf = (unsigned char *)dst + len; + k = 1; + acc = 0; + acc_len = 0; + while (len != 0) { + uint32_t w; + + w = (k <= xlen) ? x[k] : 0; + k ++; + if (acc_len == 0) { + acc = w; + acc_len = 31; + } else { + uint32_t z; + + z = acc | (w << acc_len); + acc_len --; + acc = w >> (31 - acc_len); + if (len >= 4) { + buf -= 4; + len -= 4; + mg_br_enc32be(buf, z); + } else { + switch (len) { + case 3: + buf[-3] = (unsigned char)(z >> 16); + /* fall through */ + case 2: + buf[-2] = (unsigned char)(z >> 8); + /* fall through */ + case 1: + buf[-1] = (unsigned char)z; + break; + } + return; + } + } + } +} + +// From BearSSL src/int/i31_fmont.c +void +mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i) +{ + size_t len, u, v; + + len = (m[0] + 31) >> 5; + for (u = 0; u < len; u ++) { + uint32_t f; + uint64_t cc; + + f = MG_BR_MUL31_lo(x[1], m0i); + cc = 0; + for (v = 0; v < len; v ++) { + uint64_t z; + + z = (uint64_t)x[v + 1] + MG_BR_MUL31(f, m[v + 1]) + cc; + cc = z >> 31; + if (v != 0) { + x[v] = (uint32_t)z & 0x7FFFFFFF; + } + } + x[len] = (uint32_t)cc; + } + + /* + * We may have to do an extra subtraction, but only if the + * value in x[] is indeed greater than or equal to that of m[], + * which is why we must do two calls (first call computes the + * carry, second call performs the subtraction only if the carry + * is 0). + */ + mg_br_i31_sub(x, m, MG_BR_NOT(mg_br_i31_sub(x, m, 0))); +} + +// From BearSSL src/int/i31_iszero.c +uint32_t +mg_br_i31_iszero(const uint32_t *x) +{ + uint32_t z; + size_t u; + + z = 0; + for (u = (x[0] + 31) >> 5; u > 0; u --) { + z |= x[u]; + } + return ~(z | -z) >> 31; +} + +// From BearSSL src/int/i31_modpow.c +void +mg_br_i31_modpow(uint32_t *x, + const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2) +{ + size_t mlen; + uint32_t k; + + /* + * 'mlen' is the length of m[] expressed in bytes (including + * the "bit length" first field). + */ + mlen = ((m[0] + 63) >> 5) * sizeof m[0]; + + /* + * Throughout the algorithm: + * -- t1[] is in Montgomery representation; it contains x, x^2, + * x^4, x^8... + * -- The result is accumulated, in normal representation, in + * the x[] array. + * -- t2[] is used as destination buffer for each multiplication. + * + * Note that there is no need to call mg_br_i32_from_monty(). + */ + memcpy(t1, x, mlen); + mg_br_i31_to_monty(t1, m); + mg_br_i31_zero(x, m[0]); + x[1] = 1; + for (k = 0; k < ((uint32_t)elen << 3); k ++) { + uint32_t ctl; + + ctl = (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1; + mg_br_i31_montymul(t2, x, t1, m, m0i); + MG_BR_CCOPY(ctl, x, t2, mlen); + mg_br_i31_montymul(t2, t1, t1, m, m0i); + memcpy(t1, t2, mlen); + } +} + +// From BearSSL src/int/i31_montmul.c +void +mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i) +{ + /* + * Each outer loop iteration computes: + * d <- (d + xu*y + f*m) / 2^31 + * We have xu <= 2^31-1 and f <= 2^31-1. + * Thus, if d <= 2*m-1 on input, then: + * 2*m-1 + 2*(2^31-1)*m <= (2^32)*m-1 + * and the new d value is less than 2*m. + * + * We represent d over 31-bit words, with an extra word 'dh' + * which can thus be only 0 or 1. + */ + size_t len, len4, u, v; + uint32_t dh; + + len = (m[0] + 31) >> 5; + len4 = len & ~(size_t)3; + mg_br_i31_zero(d, m[0]); + dh = 0; + for (u = 0; u < len; u ++) { + /* + * The carry for each operation fits on 32 bits: + * d[v+1] <= 2^31-1 + * xu*y[v+1] <= (2^31-1)*(2^31-1) + * f*m[v+1] <= (2^31-1)*(2^31-1) + * r <= 2^32-1 + * (2^31-1) + 2*(2^31-1)*(2^31-1) + (2^32-1) = 2^63 - 2^31 + * After division by 2^31, the new r is then at most 2^32-1 + * + * Using a 32-bit carry has performance benefits on 32-bit + * systems; however, on 64-bit architectures, we prefer to + * keep the carry (r) in a 64-bit register, thus avoiding some + * "clear high bits" operations. + */ + uint32_t f, xu; + uint32_t r; /* Mongoose: 32-bit carry only */ + + xu = x[u + 1]; + f = MG_BR_MUL31_lo((d[1] + MG_BR_MUL31_lo(x[u + 1], y[1])), m0i); + + r = 0; + for (v = 0; v < len4; v += 4) { + uint64_t z; + + z = (uint64_t)d[v + 1] + MG_BR_MUL31(xu, y[v + 1]) + + MG_BR_MUL31(f, m[v + 1]) + r; + r = (uint32_t)(z >> 31); + d[v + 0] = (uint32_t)z & 0x7FFFFFFF; + z = (uint64_t)d[v + 2] + MG_BR_MUL31(xu, y[v + 2]) + + MG_BR_MUL31(f, m[v + 2]) + r; + r = (uint32_t)(z >> 31); + d[v + 1] = (uint32_t)z & 0x7FFFFFFF; + z = (uint64_t)d[v + 3] + MG_BR_MUL31(xu, y[v + 3]) + + MG_BR_MUL31(f, m[v + 3]) + r; + r = (uint32_t)(z >> 31); + d[v + 2] = (uint32_t)z & 0x7FFFFFFF; + z = (uint64_t)d[v + 4] + MG_BR_MUL31(xu, y[v + 4]) + + MG_BR_MUL31(f, m[v + 4]) + r; + r = (uint32_t)(z >> 31); + d[v + 3] = (uint32_t)z & 0x7FFFFFFF; + } + for (; v < len; v ++) { + uint64_t z; + + z = (uint64_t)d[v + 1] + MG_BR_MUL31(xu, y[v + 1]) + + MG_BR_MUL31(f, m[v + 1]) + r; + r = (uint32_t)(z >> 31); + d[v] = (uint32_t)z & 0x7FFFFFFF; + } + + /* + * Since the new dh can only be 0 or 1, the addition of + * the old dh with the carry MUST fit on 32 bits, and + * thus can be done into dh itself. + */ + dh += r; + d[len] = dh & 0x7FFFFFFF; + dh >>= 31; + } + + /* + * We must write back the bit length because it was overwritten in + * the loop (not overwriting it would require a test in the loop, + * which would yield bigger and slower code). + */ + d[0] = m[0]; + + /* + * d[] may still be greater than m[] at that point; notably, the + * 'dh' word may be non-zero. + */ + mg_br_i31_sub(d, m, MG_BR_NEQ(dh, 0) | MG_BR_NOT(mg_br_i31_sub(d, m, 0))); +} + +// From BearSSL src/int/i31_muladd.c +void +mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m) +{ + uint32_t m_bitlen; + unsigned mblr; + size_t u, mlen; + uint32_t a0, a1, b0, hi, g, q, tb; + uint32_t under, over; + uint32_t cc; + + /* + * We can test on the modulus bit length since we accept to + * leak that length. + */ + m_bitlen = m[0]; + if (m_bitlen == 0) { + return; + } + if (m_bitlen <= 31) { + uint32_t lo; + + hi = x[1] >> 1; + lo = (x[1] << 31) | z; + x[1] = mg_br_rem(hi, lo, m[1]); + return; + } + mlen = (m_bitlen + 31) >> 5; + mblr = (unsigned)m_bitlen & 31; + + /* + * Principle: we estimate the quotient (x*2^31+z)/m by + * doing a 64/32 division with the high words. + * + * Let: + * w = 2^31 + * a = (w*a0 + a1) * w^N + a2 + * b = b0 * w^N + b2 + * such that: + * 0 <= a0 < w + * 0 <= a1 < w + * 0 <= a2 < w^N + * w/2 <= b0 < w + * 0 <= b2 < w^N + * a < w*b + * I.e. the two top words of a are a0:a1, the top word of b is + * b0, we ensured that b0 is "full" (high bit set), and a is + * such that the quotient q = a/b fits on one word (0 <= q < w). + * + * If a = b*q + r (with 0 <= r < q), we can estimate q by + * doing an Euclidean division on the top words: + * a0*w+a1 = b0*u + v (with 0 <= v < b0) + * Then the following holds: + * 0 <= u <= w + * u-2 <= q <= u + */ + hi = x[mlen]; + if (mblr == 0) { + a0 = x[mlen]; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a1 = x[mlen]; + b0 = m[mlen]; + } else { + a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) + & 0x7FFFFFFF; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) + & 0x7FFFFFFF; + b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr)) + & 0x7FFFFFFF; + } + + /* + * We estimate a divisor q. If the quotient returned by mg_br_div() + * is g: + * -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF. + * -- Otherwise: + * -- if g == 0 then we set q = 0; + * -- otherwise, we set q = g - 1. + * The properties described above then ensure that the true + * quotient is q-1, q or q+1. + * + * Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We + * must adjust the parameters to mg_br_div() accordingly. + */ + g = mg_br_div(a0 >> 1, a1 | (a0 << 31), b0); + q = MG_BR_MUX(MG_BR_EQ(a0, b0), 0x7FFFFFFF, MG_BR_MUX(MG_BR_EQ(g, 0), 0, g - 1)); + + /* + * We subtract q*m from x (with the extra high word of value 'hi'). + * Since q may be off by 1 (in either direction), we may have to + * add or subtract m afterwards. + * + * The 'tb' flag will be true (1) at the end of the loop if the + * result is greater than or equal to the modulus (not counting + * 'hi' or the carry). + */ + cc = 0; + tb = 1; + for (u = 1; u <= mlen; u ++) { + uint32_t mw, zw, xw, nxw; + uint64_t zl; + + mw = m[u]; + zl = MG_BR_MUL31(mw, q) + cc; + cc = (uint32_t)(zl >> 31); + zw = (uint32_t)zl & (uint32_t)0x7FFFFFFF; + xw = x[u]; + nxw = xw - zw; + cc += nxw >> 31; + nxw &= 0x7FFFFFFF; + x[u] = nxw; + tb = MG_BR_MUX(MG_BR_EQ(nxw, mw), tb, MG_BR_GT(nxw, mw)); + } + + /* + * If we underestimated q, then either cc < hi (one extra bit + * beyond the top array word), or cc == hi and tb is true (no + * extra bit, but the result is not lower than the modulus). In + * these cases we must subtract m once. + * + * Otherwise, we may have overestimated, which will show as + * cc > hi (thus a negative result). Correction is adding m once. + */ + over = MG_BR_GT(cc, hi); + under = ~over & (tb | MG_BR_LT(cc, hi)); + mg_br_i31_add(x, m, over); + mg_br_i31_sub(x, m, under); +} + +// From BearSSL src/int/i31_ninv31.c +uint32_t +mg_br_i31_ninv31(uint32_t x) +{ + uint32_t y; + + y = 2 - x; + y *= 2 - y * x; + y *= 2 - y * x; + y *= 2 - y * x; + y *= 2 - y * x; + return MG_BR_MUX(x & 1, -y, 0) & 0x7FFFFFFF; +} + +// From BearSSL src/int/i31_rshift.c +void +mg_br_i31_rshift(uint32_t *x, int count) +{ + size_t u, len; + uint32_t r; + + len = (x[0] + 31) >> 5; + if (len == 0) { + return; + } + r = x[1] >> count; + for (u = 2; u <= len; u ++) { + uint32_t w; + + w = x[u]; + x[u - 1] = ((w << (31 - count)) | r) & 0x7FFFFFFF; + r = w >> count; + } + x[len] = r; +} + +// From BearSSL src/int/i31_sub.c +uint32_t +mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 63) >> 5; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = b[u]; + naw = aw - bw - cc; + cc = naw >> 31; + a[u] = MG_BR_MUX(ctl, naw & 0x7FFFFFFF, aw); + } + return cc; +} + +// From BearSSL src/int/i31_tmont.c +void +mg_br_i31_to_monty(uint32_t *x, const uint32_t *m) +{ + uint32_t k; + + for (k = (m[0] + 31) >> 5; k > 0; k --) { + mg_br_i31_muladd_small(x, 0, m); + } +} + +// From BearSSL src/ec/ecdsa_i31_bits.c +void +mg_br_ecdsa_i31_bits2int(uint32_t *x, + const void *src, size_t len, uint32_t ebitlen) +{ + uint32_t bitlen, hbitlen; + int sc; + + bitlen = ebitlen - (ebitlen >> 5); + hbitlen = (uint32_t)len << 3; + if (hbitlen > bitlen) { + len = (bitlen + 7) >> 3; + sc = (int)((hbitlen - bitlen) & 7); + } else { + sc = 0; + } + mg_br_i31_zero(x, ebitlen); + mg_br_i31_decode(x, src, len); + mg_br_i31_rshift(x, sc); + x[0] = ebitlen; +} + +// From BearSSL src/ec/ecdsa_i31_vrfy_raw.c +#define I31_LEN ((MG_BR_MAX_EC_SIZE + 61) / 31) +#define POINT_LEN (1 + (((MG_BR_MAX_EC_SIZE + 7) >> 3) << 1)) + +uint32_t +mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash, size_t hash_len, + const unsigned char *q, size_t qlen, + const void *sig, size_t sig_len) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + */ + const unsigned char *order; + uint32_t n[I31_LEN], r[I31_LEN], s[I31_LEN], t1[I31_LEN], t2[I31_LEN]; + unsigned char tx[(MG_BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char ty[(MG_BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char eU[POINT_LEN]; + size_t nlen, rlen, ulen; + uint32_t n0i, res; + + /* + * If the curve is not supported, then report an error. + */ + if (((impl->supported_curves >> curve) & 1) == 0) { + return 0; + } + + /* + * Get the curve parameters (generator and order). + */ + order = impl->order(curve, &nlen); + impl->generator(curve, &ulen); + + /* + * Signature length must be even. + */ + if (sig_len & 1) { + return 0; + } + rlen = sig_len >> 1; + + /* + * Public key point must have the proper size for this curve. + */ + if (qlen != ulen) { + return 0; + } + + /* + * Get modulus; then decode the r and s values. They must be + * lower than the modulus, and s must not be null. + */ + mg_br_i31_decode(n, order, nlen); + n0i = mg_br_i31_ninv31(n[1]); + if (!mg_br_i31_decode_mod(r, sig, rlen, n)) { + return 0; + } + if (!mg_br_i31_decode_mod(s, (const unsigned char *)sig + rlen, rlen, n)) { + return 0; + } + if (mg_br_i31_iszero(s)) { + return 0; + } + + /* + * Invert s. We do that with a modular exponentiation; we use + * the fact that for all the curves we support, the least + * significant byte is not 0 or 1, so we can subtract 2 without + * any carry to process. + * We also want 1/s in Montgomery representation, which can be + * done by converting _from_ Montgomery representation before + * the inversion (because (1/s)*R = 1/(s/R)). + */ + mg_br_i31_from_monty(s, n, n0i); + memcpy(tx, order, nlen); + tx[nlen - 1] -= 2; + mg_br_i31_modpow(s, tx, nlen, n, n0i, t1, t2); + + /* + * Truncate the hash to the modulus length (in bits) and reduce + * it modulo the curve order. The modular reduction can be done + * with a subtraction since the truncation already reduced the + * value to the modulus bit length. + */ + mg_br_ecdsa_i31_bits2int(t1, hash, hash_len, n[0]); + mg_br_i31_sub(t1, n, mg_br_i31_sub(t1, n, 0) ^ 1); + + /* + * Multiply the (truncated, reduced) hash value with 1/s, result in + * t2, encoded in ty. + */ + mg_br_i31_montymul(t2, t1, s, n, n0i); + mg_br_i31_encode(ty, nlen, t2); + + /* + * Multiply r with 1/s, result in t1, encoded in tx. + */ + mg_br_i31_montymul(t1, r, s, n, n0i); + mg_br_i31_encode(tx, nlen, t1); + + /* + * Compute the point x*Q + y*G. + */ + memcpy(eU, q, ulen); + res = impl->muladd(eU, NULL, ulen, + tx, nlen, ty, nlen, curve); + + /* + * Get the X coordinate, reduce modulo the curve order, and + * compare with the 'r' value. + * + * The modular reduction can be done with subtractions because + * we work with curves of prime order, so the curve order is + * close to the field order (Hasse's theorem). + */ + mg_br_i31_zero(t1, n[0]); + mg_br_i31_decode(t1, &eU[1], ulen >> 1); + t1[0] = n[0]; + mg_br_i31_sub(t1, n, mg_br_i31_sub(t1, n, 0) ^ 1); + res &= ~mg_br_i31_sub(t1, r, 1); + res &= mg_br_i31_iszero(t1); + return res; +} + +// From BearSSL src/ec/ecdsa_i31_sign_raw.c, k is given by the caller +#define ORDER_LEN ((MG_BR_MAX_EC_SIZE + 7) >> 3) + +size_t +mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash_value, size_t hash_len, + const unsigned char *xb, size_t xlen, + const unsigned char *kb, void *sig) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + * We also rely on the last byte of the curve order to be distinct + * from 0 and 1. + */ + const unsigned char *order; + uint32_t n[I31_LEN], r[I31_LEN], s[I31_LEN], x[I31_LEN]; + uint32_t m[I31_LEN], k[I31_LEN], t1[I31_LEN], t2[I31_LEN]; + unsigned char tt[ORDER_LEN << 1]; + unsigned char eU[POINT_LEN]; + size_t nlen, ulen; + uint32_t n0i, ctl; + + /* + * If the curve is not supported, then exit with an error. + */ + if (((impl->supported_curves >> curve) & 1) == 0) { + return 0; + } + + /* + * Get modulus. + */ + order = impl->order(curve, &nlen); + mg_br_i31_decode(n, order, nlen); + n0i = mg_br_i31_ninv31(n[1]); + + /* + * Get private key as an i31 integer. This also checks that the + * private key is well-defined (not zero, and less than the + * curve order). + */ + if (!mg_br_i31_decode_mod(x, xb, xlen, n)) { + return 0; + } + if (mg_br_i31_iszero(x)) { + return 0; + } + + /* + * Truncate and reduce the hash value modulo the curve order. + */ + mg_br_ecdsa_i31_bits2int(m, hash_value, hash_len, n[0]); + mg_br_i31_sub(m, n, mg_br_i31_sub(m, n, 0) ^ 1); + + /* + * Mongoose: the "k" value is given by the caller (e.g. RFC 6979, + * computed by uECC). It must be in [1, n-1]. + */ + if (!mg_br_i31_decode_mod(k, kb, nlen, n) || mg_br_i31_iszero(k)) { + return 0; + } + + /* + * Compute k*G and extract the X coordinate, then reduce it + * modulo the curve order. Since we support only curves with + * prime order, that reduction is only a matter of computing + * a subtraction. + */ + mg_br_i31_encode(tt, nlen, k); + ulen = impl->mulgen(eU, tt, nlen, curve); + mg_br_i31_zero(r, n[0]); + mg_br_i31_decode(r, &eU[1], ulen >> 1); + r[0] = n[0]; + mg_br_i31_sub(r, n, mg_br_i31_sub(r, n, 0) ^ 1); + + /* + * Compute 1/k in double-Montgomery representation. We do so by + * first converting _from_ Montgomery representation (twice), + * then using a modular exponentiation. + */ + mg_br_i31_from_monty(k, n, n0i); + mg_br_i31_from_monty(k, n, n0i); + memcpy(tt, order, nlen); + tt[nlen - 1] -= 2; + mg_br_i31_modpow(k, tt, nlen, n, n0i, t1, t2); + + /* + * Compute s = (m+xr)/k (mod n). + * The k[] array contains R^2/k (double-Montgomery representation); + * we thus can use direct Montgomery multiplications and conversions + * from Montgomery, avoiding any call to mg_br_i31_to_monty() (which + * is slower). + */ + mg_br_i31_from_monty(m, n, n0i); + mg_br_i31_montymul(t1, x, r, n, n0i); + ctl = mg_br_i31_add(t1, m, 1); + ctl |= mg_br_i31_sub(t1, n, 0) ^ 1; + mg_br_i31_sub(t1, n, ctl); + mg_br_i31_montymul(s, t1, k, n, n0i); + + /* + * Encode r and s in the signature. + */ + mg_br_i31_encode(sig, nlen, r); + mg_br_i31_encode((unsigned char *)sig + nlen, nlen, s); + return nlen << 1; +} + +#if MG_P256 == MG_P256_BEAR +// From BearSSL src/ec/ec_p256_m31.c +/* + * If MG_BR_NO_ARITH_SHIFT is undefined, or defined to 0, then we _assume_ + * that right-shifting a signed negative integer copies the sign bit + * (arithmetic right-shift). This is "implementation-defined behaviour", + * i.e. it is not undefined, but it may differ between compilers. Each + * compiler is supposed to document its behaviour in that respect. GCC + * explicitly defines that an arithmetic right shift is used. We expect + * all other compilers to do the same, because underlying CPU offer an + * arithmetic right shift opcode that could not be used otherwise. + */ +#define MG_BR_NO_ARITH_SHIFT 0 +#if MG_BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#define ARSHW(x, n) (((uint64_t)(x) >> (n)) \ + | ((-((uint64_t)(x) >> 63)) << (64 - (n)))) +#else +#define ARSH(x, n) ((uint32_t)((*(int32_t *)&(x)) >> (n))) +#define ARSHW(x, n) ((uint64_t)((*(int64_t *)&(x)) >> (n))) +#endif + +/* + * Convert an integer from unsigned big-endian encoding to a sequence of + * 30-bit words in little-endian order. The final "partial" word is + * returned. + */ +static uint32_t +be8_to_le30(uint32_t *dst, const unsigned char *src, size_t len) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + uint32_t b; + + b = src[len]; + if (acc_len < 22) { + acc |= b << acc_len; + acc_len += 8; + } else { + *dst ++ = (acc | (b << acc_len)) & 0x3FFFFFFF; + acc = b >> (30 - acc_len); + acc_len -= 22; + } + } + return acc; +} + +/* + * Convert an integer (30-bit words, little-endian) to unsigned + * big-endian encoding. The total encoding length is provided; all + * the destination bytes will be filled. + */ +static void +le30_to_be8(unsigned char *dst, size_t len, const uint32_t *src) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + if (acc_len < 8) { + uint32_t w; + + w = *src ++; + dst[len] = (unsigned char)(acc | (w << acc_len)); + acc = w >> (8 - acc_len); + acc_len += 22; + } else { + dst[len] = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } + } +} + +/* + * Multiply two integers. Source integers are represented as arrays of + * nine 30-bit words, for values up to 2^270-1. Result is encoded over + * 18 words of 30 bits each. + */ +static void +mul9(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + /* + * Maximum intermediate result is no more than + * 10376293531797946367, which fits in 64 bits. Reason: + * + * 10376293531797946367 = 9 * (2^30-1)^2 + 9663676406 + * 10376293531797946367 < 9663676407 * 2^30 + * + * Thus, adding together 9 products of 30-bit integers, with + * a carry of at most 9663676406, yields an integer that fits + * on 64 bits and generates a carry of at most 9663676406. + */ + uint64_t t[17]; + uint64_t cc; + int i; + + t[ 0] = MG_BR_MUL31(a[0], b[0]); + t[ 1] = MG_BR_MUL31(a[0], b[1]) + + MG_BR_MUL31(a[1], b[0]); + t[ 2] = MG_BR_MUL31(a[0], b[2]) + + MG_BR_MUL31(a[1], b[1]) + + MG_BR_MUL31(a[2], b[0]); + t[ 3] = MG_BR_MUL31(a[0], b[3]) + + MG_BR_MUL31(a[1], b[2]) + + MG_BR_MUL31(a[2], b[1]) + + MG_BR_MUL31(a[3], b[0]); + t[ 4] = MG_BR_MUL31(a[0], b[4]) + + MG_BR_MUL31(a[1], b[3]) + + MG_BR_MUL31(a[2], b[2]) + + MG_BR_MUL31(a[3], b[1]) + + MG_BR_MUL31(a[4], b[0]); + t[ 5] = MG_BR_MUL31(a[0], b[5]) + + MG_BR_MUL31(a[1], b[4]) + + MG_BR_MUL31(a[2], b[3]) + + MG_BR_MUL31(a[3], b[2]) + + MG_BR_MUL31(a[4], b[1]) + + MG_BR_MUL31(a[5], b[0]); + t[ 6] = MG_BR_MUL31(a[0], b[6]) + + MG_BR_MUL31(a[1], b[5]) + + MG_BR_MUL31(a[2], b[4]) + + MG_BR_MUL31(a[3], b[3]) + + MG_BR_MUL31(a[4], b[2]) + + MG_BR_MUL31(a[5], b[1]) + + MG_BR_MUL31(a[6], b[0]); + t[ 7] = MG_BR_MUL31(a[0], b[7]) + + MG_BR_MUL31(a[1], b[6]) + + MG_BR_MUL31(a[2], b[5]) + + MG_BR_MUL31(a[3], b[4]) + + MG_BR_MUL31(a[4], b[3]) + + MG_BR_MUL31(a[5], b[2]) + + MG_BR_MUL31(a[6], b[1]) + + MG_BR_MUL31(a[7], b[0]); + t[ 8] = MG_BR_MUL31(a[0], b[8]) + + MG_BR_MUL31(a[1], b[7]) + + MG_BR_MUL31(a[2], b[6]) + + MG_BR_MUL31(a[3], b[5]) + + MG_BR_MUL31(a[4], b[4]) + + MG_BR_MUL31(a[5], b[3]) + + MG_BR_MUL31(a[6], b[2]) + + MG_BR_MUL31(a[7], b[1]) + + MG_BR_MUL31(a[8], b[0]); + t[ 9] = MG_BR_MUL31(a[1], b[8]) + + MG_BR_MUL31(a[2], b[7]) + + MG_BR_MUL31(a[3], b[6]) + + MG_BR_MUL31(a[4], b[5]) + + MG_BR_MUL31(a[5], b[4]) + + MG_BR_MUL31(a[6], b[3]) + + MG_BR_MUL31(a[7], b[2]) + + MG_BR_MUL31(a[8], b[1]); + t[10] = MG_BR_MUL31(a[2], b[8]) + + MG_BR_MUL31(a[3], b[7]) + + MG_BR_MUL31(a[4], b[6]) + + MG_BR_MUL31(a[5], b[5]) + + MG_BR_MUL31(a[6], b[4]) + + MG_BR_MUL31(a[7], b[3]) + + MG_BR_MUL31(a[8], b[2]); + t[11] = MG_BR_MUL31(a[3], b[8]) + + MG_BR_MUL31(a[4], b[7]) + + MG_BR_MUL31(a[5], b[6]) + + MG_BR_MUL31(a[6], b[5]) + + MG_BR_MUL31(a[7], b[4]) + + MG_BR_MUL31(a[8], b[3]); + t[12] = MG_BR_MUL31(a[4], b[8]) + + MG_BR_MUL31(a[5], b[7]) + + MG_BR_MUL31(a[6], b[6]) + + MG_BR_MUL31(a[7], b[5]) + + MG_BR_MUL31(a[8], b[4]); + t[13] = MG_BR_MUL31(a[5], b[8]) + + MG_BR_MUL31(a[6], b[7]) + + MG_BR_MUL31(a[7], b[6]) + + MG_BR_MUL31(a[8], b[5]); + t[14] = MG_BR_MUL31(a[6], b[8]) + + MG_BR_MUL31(a[7], b[7]) + + MG_BR_MUL31(a[8], b[6]); + t[15] = MG_BR_MUL31(a[7], b[8]) + + MG_BR_MUL31(a[8], b[7]); + t[16] = MG_BR_MUL31(a[8], b[8]); + + /* + * Propagate carries. + */ + cc = 0; + for (i = 0; i < 17; i ++) { + uint64_t w; + + w = t[i] + cc; + d[i] = (uint32_t)w & 0x3FFFFFFF; + cc = w >> 30; + } + d[17] = (uint32_t)cc; +} + +/* + * Square a 270-bit integer, represented as an array of nine 30-bit words. + * Result uses 18 words of 30 bits each. + */ +static void +square9(uint32_t *d, const uint32_t *a) +{ + uint64_t t[17]; + uint64_t cc; + int i; + + t[ 0] = MG_BR_MUL31(a[0], a[0]); + t[ 1] = ((MG_BR_MUL31(a[0], a[1])) << 1); + t[ 2] = MG_BR_MUL31(a[1], a[1]) + + ((MG_BR_MUL31(a[0], a[2])) << 1); + t[ 3] = ((MG_BR_MUL31(a[0], a[3]) + + MG_BR_MUL31(a[1], a[2])) << 1); + t[ 4] = MG_BR_MUL31(a[2], a[2]) + + ((MG_BR_MUL31(a[0], a[4]) + + MG_BR_MUL31(a[1], a[3])) << 1); + t[ 5] = ((MG_BR_MUL31(a[0], a[5]) + + MG_BR_MUL31(a[1], a[4]) + + MG_BR_MUL31(a[2], a[3])) << 1); + t[ 6] = MG_BR_MUL31(a[3], a[3]) + + ((MG_BR_MUL31(a[0], a[6]) + + MG_BR_MUL31(a[1], a[5]) + + MG_BR_MUL31(a[2], a[4])) << 1); + t[ 7] = ((MG_BR_MUL31(a[0], a[7]) + + MG_BR_MUL31(a[1], a[6]) + + MG_BR_MUL31(a[2], a[5]) + + MG_BR_MUL31(a[3], a[4])) << 1); + t[ 8] = MG_BR_MUL31(a[4], a[4]) + + ((MG_BR_MUL31(a[0], a[8]) + + MG_BR_MUL31(a[1], a[7]) + + MG_BR_MUL31(a[2], a[6]) + + MG_BR_MUL31(a[3], a[5])) << 1); + t[ 9] = ((MG_BR_MUL31(a[1], a[8]) + + MG_BR_MUL31(a[2], a[7]) + + MG_BR_MUL31(a[3], a[6]) + + MG_BR_MUL31(a[4], a[5])) << 1); + t[10] = MG_BR_MUL31(a[5], a[5]) + + ((MG_BR_MUL31(a[2], a[8]) + + MG_BR_MUL31(a[3], a[7]) + + MG_BR_MUL31(a[4], a[6])) << 1); + t[11] = ((MG_BR_MUL31(a[3], a[8]) + + MG_BR_MUL31(a[4], a[7]) + + MG_BR_MUL31(a[5], a[6])) << 1); + t[12] = MG_BR_MUL31(a[6], a[6]) + + ((MG_BR_MUL31(a[4], a[8]) + + MG_BR_MUL31(a[5], a[7])) << 1); + t[13] = ((MG_BR_MUL31(a[5], a[8]) + + MG_BR_MUL31(a[6], a[7])) << 1); + t[14] = MG_BR_MUL31(a[7], a[7]) + + ((MG_BR_MUL31(a[6], a[8])) << 1); + t[15] = ((MG_BR_MUL31(a[7], a[8])) << 1); + t[16] = MG_BR_MUL31(a[8], a[8]); + + /* + * Propagate carries. + */ + cc = 0; + for (i = 0; i < 17; i ++) { + uint64_t w; + + w = t[i] + cc; + d[i] = (uint32_t)w & 0x3FFFFFFF; + cc = w >> 30; + } + d[17] = (uint32_t)cc; +} + +/* + * Base field modulus for P-256. + */ +static const uint32_t F256[] = { + + 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x0000003F, 0x00000000, + 0x00000000, 0x00001000, 0x3FFFC000, 0x0000FFFF +}; + +/* + * The 'b' curve equation coefficient for P-256. + */ +static const uint32_t P256_B[] = { + + 0x27D2604B, 0x2F38F0F8, 0x053B0F63, 0x0741AC33, 0x1886BC65, + 0x2EF555DA, 0x293E7B3E, 0x0D762A8E, 0x00005AC6 +}; + +/* + * Addition in the field. Source operands shall fit on 257 bits; output + * will be lower than twice the modulus. + */ +static void +add_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t w, cc; + int i; + + cc = 0; + for (i = 0; i < 9; i ++) { + w = a[i] + b[i] + cc; + d[i] = w & 0x3FFFFFFF; + cc = w >> 30; + } + w >>= 16; + d[8] &= 0xFFFF; + d[3] -= w << 6; + d[6] -= w << 12; + d[7] += w << 14; + cc = w; + for (i = 0; i < 9; i ++) { + w = d[i] + cc; + d[i] = w & 0x3FFFFFFF; + cc = ARSH(w, 30); + } +} + +/* + * Subtraction in the field. Source operands shall be smaller than twice + * the modulus; the result will fulfil the same property. + */ +static void +sub_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t w, cc; + int i; + + /* + * We really compute a - b + 2*p to make sure that the result is + * positive. + */ + w = a[0] - b[0] - 0x00002; + d[0] = w & 0x3FFFFFFF; + w = a[1] - b[1] + ARSH(w, 30); + d[1] = w & 0x3FFFFFFF; + w = a[2] - b[2] + ARSH(w, 30); + d[2] = w & 0x3FFFFFFF; + w = a[3] - b[3] + ARSH(w, 30) + 0x00080; + d[3] = w & 0x3FFFFFFF; + w = a[4] - b[4] + ARSH(w, 30); + d[4] = w & 0x3FFFFFFF; + w = a[5] - b[5] + ARSH(w, 30); + d[5] = w & 0x3FFFFFFF; + w = a[6] - b[6] + ARSH(w, 30) + 0x02000; + d[6] = w & 0x3FFFFFFF; + w = a[7] - b[7] + ARSH(w, 30) - 0x08000; + d[7] = w & 0x3FFFFFFF; + w = a[8] - b[8] + ARSH(w, 30) + 0x20000; + d[8] = w & 0xFFFF; + w >>= 16; + d[8] &= 0xFFFF; + d[3] -= w << 6; + d[6] -= w << 12; + d[7] += w << 14; + cc = w; + for (i = 0; i < 9; i ++) { + w = d[i] + cc; + d[i] = w & 0x3FFFFFFF; + cc = ARSH(w, 30); + } +} + +/* + * Compute a multiplication in F256. Source operands shall be less than + * twice the modulus. + */ +static void +mul_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[18]; + uint64_t s[18]; + uint64_t cc, x; + uint32_t z, c; + int i; + + mul9(t, a, b); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + * + * We use 64-bit intermediate words to allow for carries to + * accumulate easily, before performing the final propagation. + */ + for (i = 0; i < 18; i ++) { + s[i] = t[i]; + } + + for (i = 17; i >= 9; i --) { + uint64_t y; + + y = s[i]; + s[i - 1] += ARSHW(y, 2); + s[i - 2] += (y << 28) & 0x3FFFFFFF; + s[i - 2] -= ARSHW(y, 4); + s[i - 3] -= (y << 26) & 0x3FFFFFFF; + s[i - 5] -= ARSHW(y, 10); + s[i - 6] -= (y << 20) & 0x3FFFFFFF; + s[i - 8] += ARSHW(y, 16); + s[i - 9] += (y << 14) & 0x3FFFFFFF; + } + + /* + * Carry propagation must be signed. Moreover, we may have overdone + * it a bit, and obtain a negative result. + * + * The loop above ran 9 times; each time, each word was augmented + * by at most one extra word (in absolute value). Thus, the top + * word must in fine fit in 39 bits, so the carry below will fit + * on 9 bits. + */ + cc = 0; + for (i = 0; i < 9; i ++) { + x = s[i] + cc; + d[i] = (uint32_t)x & 0x3FFFFFFF; + cc = ARSHW(x, 30); + } + + /* + * All nine words fit on 30 bits, but there may be an extra + * carry for a few bits (at most 9), and that carry may be + * negative. Moreover, we want the result to fit on 257 bits. + * The two lines below ensure that the word in d[] has length + * 256 bits, and the (signed) carry (beyond 2^256) is in cc. The + * significant length of cc is less than 24 bits, so we will be + * able to switch to 32-bit operations. + */ + cc = ARSHW(x, 16); + d[8] &= 0xFFFF; + + /* + * One extra round of reduction, for cc*2^256, which means + * adding cc*(2^224-2^192-2^96+1) to a 256-bit (nonnegative) + * value. If cc is negative, then it may happen (rarely, but + * not neglectibly so) that the result would be negative. In + * order to avoid that, if cc is negative, then we add the + * modulus once. Note that if cc is negative, then propagating + * that carry must yield a value lower than the modulus, so + * adding the modulus once will keep the final result under + * twice the modulus. + */ + z = (uint32_t)cc; + d[3] -= z << 6; + d[6] -= (z << 12) & 0x3FFFFFFF; + d[7] -= ARSH(z, 18); + d[7] += (z << 14) & 0x3FFFFFFF; + d[8] += ARSH(z, 16); + c = z >> 31; + d[0] -= c; + d[3] += c << 6; + d[6] += c << 12; + d[7] -= c << 14; + d[8] += c << 16; + for (i = 0; i < 9; i ++) { + uint32_t w; + + w = d[i] + z; + d[i] = w & 0x3FFFFFFF; + z = ARSH(w, 30); + } +} + +/* + * Compute a square in F256. Source operand shall be less than + * twice the modulus. + */ +static void +square_f256(uint32_t *d, const uint32_t *a) +{ + uint32_t t[18]; + uint64_t s[18]; + uint64_t cc, x; + uint32_t z, c; + int i; + + square9(t, a); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + * + * We use 64-bit intermediate words to allow for carries to + * accumulate easily, before performing the final propagation. + */ + for (i = 0; i < 18; i ++) { + s[i] = t[i]; + } + + for (i = 17; i >= 9; i --) { + uint64_t y; + + y = s[i]; + s[i - 1] += ARSHW(y, 2); + s[i - 2] += (y << 28) & 0x3FFFFFFF; + s[i - 2] -= ARSHW(y, 4); + s[i - 3] -= (y << 26) & 0x3FFFFFFF; + s[i - 5] -= ARSHW(y, 10); + s[i - 6] -= (y << 20) & 0x3FFFFFFF; + s[i - 8] += ARSHW(y, 16); + s[i - 9] += (y << 14) & 0x3FFFFFFF; + } + + /* + * Carry propagation must be signed. Moreover, we may have overdone + * it a bit, and obtain a negative result. + * + * The loop above ran 9 times; each time, each word was augmented + * by at most one extra word (in absolute value). Thus, the top + * word must in fine fit in 39 bits, so the carry below will fit + * on 9 bits. + */ + cc = 0; + for (i = 0; i < 9; i ++) { + x = s[i] + cc; + d[i] = (uint32_t)x & 0x3FFFFFFF; + cc = ARSHW(x, 30); + } + + /* + * All nine words fit on 30 bits, but there may be an extra + * carry for a few bits (at most 9), and that carry may be + * negative. Moreover, we want the result to fit on 257 bits. + * The two lines below ensure that the word in d[] has length + * 256 bits, and the (signed) carry (beyond 2^256) is in cc. The + * significant length of cc is less than 24 bits, so we will be + * able to switch to 32-bit operations. + */ + cc = ARSHW(x, 16); + d[8] &= 0xFFFF; + + /* + * One extra round of reduction, for cc*2^256, which means + * adding cc*(2^224-2^192-2^96+1) to a 256-bit (nonnegative) + * value. If cc is negative, then it may happen (rarely, but + * not neglectibly so) that the result would be negative. In + * order to avoid that, if cc is negative, then we add the + * modulus once. Note that if cc is negative, then propagating + * that carry must yield a value lower than the modulus, so + * adding the modulus once will keep the final result under + * twice the modulus. + */ + z = (uint32_t)cc; + d[3] -= z << 6; + d[6] -= (z << 12) & 0x3FFFFFFF; + d[7] -= ARSH(z, 18); + d[7] += (z << 14) & 0x3FFFFFFF; + d[8] += ARSH(z, 16); + c = z >> 31; + d[0] -= c; + d[3] += c << 6; + d[6] += c << 12; + d[7] -= c << 14; + d[8] += c << 16; + for (i = 0; i < 9; i ++) { + uint32_t w; + + w = d[i] + z; + d[i] = w & 0x3FFFFFFF; + z = ARSH(w, 30); + } +} + +/* + * Perform a "final reduction" in field F256 (field for curve P-256). + * The source value must be less than twice the modulus. If the value + * is not lower than the modulus, then the modulus is subtracted and + * this function returns 1; otherwise, it leaves it untouched and it + * returns 0. + */ +static uint32_t +reduce_final_f256(uint32_t *d) +{ + uint32_t t[9]; + uint32_t cc; + int i; + + cc = 0; + for (i = 0; i < 9; i ++) { + uint32_t w; + + w = d[i] - F256[i] - cc; + cc = w >> 31; + t[i] = w & 0x3FFFFFFF; + } + cc ^= 1; + MG_BR_CCOPY(cc, d, t, sizeof t); + return cc; +} + +/* + * Jacobian coordinates for a point in P-256: affine coordinates (X,Y) + * are such that: + * X = x / z^2 + * Y = y / z^3 + * For the point at infinity, z = 0. + * Each point thus admits many possible representations. + * + * Coordinates are represented in arrays of 32-bit integers, each holding + * 30 bits of data. Values may also be slightly greater than the modulus, + * but they will always be lower than twice the modulus. + */ +struct p256_jacobian { + uint32_t x[9]; + uint32_t y[9]; + uint32_t z[9]; +}; + +/* + * Convert a point to affine coordinates: + * - If the point is the point at infinity, then all three coordinates + * are set to 0. + * - Otherwise, the 'z' coordinate is set to 1, and the 'x' and 'y' + * coordinates are the 'X' and 'Y' affine coordinates. + * The coordinates are guaranteed to be lower than the modulus. + */ +static void +p256_to_affine(struct p256_jacobian *P) +{ + uint32_t t1[9], t2[9]; + int i; + + /* + * Invert z with a modular exponentiation: the modulus is + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1, and the exponent is + * p-2. Exponent bit pattern (from high to low) is: + * - 32 bits of value 1 + * - 31 bits of value 0 + * - 1 bit of value 1 + * - 96 bits of value 0 + * - 94 bits of value 1 + * - 1 bit of value 0 + * - 1 bit of value 1 + * Thus, we precompute z^(2^31-1) to speed things up. + * + * If z = 0 (point at infinity) then the modular exponentiation + * will yield 0, which leads to the expected result (all three + * coordinates set to 0). + */ + + /* + * A simple square-and-multiply for z^(2^31-1). We could save about + * two dozen multiplications here with an addition chain, but + * this would require a bit more code, and extra stack buffers. + */ + memcpy(t1, P->z, sizeof P->z); + for (i = 0; i < 30; i ++) { + square_f256(t1, t1); + mul_f256(t1, t1, P->z); + } + + /* + * Square-and-multiply. Apart from the squarings, we have a few + * multiplications to set bits to 1; we multiply by the original z + * for setting 1 bit, and by t1 for setting 31 bits. + */ + memcpy(t2, P->z, sizeof P->z); + for (i = 1; i < 256; i ++) { + square_f256(t2, t2); + switch (i) { + case 31: + case 190: + case 221: + case 252: + mul_f256(t2, t2, t1); + break; + case 63: + case 253: + case 255: + mul_f256(t2, t2, P->z); + break; + } + } + + /* + * Now that we have 1/z, multiply x by 1/z^2 and y by 1/z^3. + */ + mul_f256(t1, t2, t2); + mul_f256(P->x, t1, P->x); + mul_f256(t1, t1, t2); + mul_f256(P->y, t1, P->y); + reduce_final_f256(P->x); + reduce_final_f256(P->y); + + /* + * Multiply z by 1/z. If z = 0, then this will yield 0, otherwise + * this will set z to 1. + */ + mul_f256(P->z, P->z, t2); + reduce_final_f256(P->z); +} + +/* + * Double a point in P-256. This function works for all valid points, + * including the point at infinity. + */ +static void +p256_double(struct p256_jacobian *Q) +{ + /* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * These formulas work for all points, including points of order 2 + * and points at infinity: + * - If y = 0 then z' = 0. But there is no such point in P-256 + * anyway. + * - If z = 0 then z' = 0. + */ + uint32_t t1[9], t2[9], t3[9], t4[9]; + + /* + * Compute z^2 in t1. + */ + square_f256(t1, Q->z); + + /* + * Compute x-z^2 in t2 and x+z^2 in t1. + */ + add_f256(t2, Q->x, t1); + sub_f256(t1, Q->x, t1); + + /* + * Compute 3*(x+z^2)*(x-z^2) in t1. + */ + mul_f256(t3, t1, t2); + add_f256(t1, t3, t3); + add_f256(t1, t3, t1); + + /* + * Compute 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + square_f256(t3, Q->y); + add_f256(t3, t3, t3); + mul_f256(t2, Q->x, t3); + add_f256(t2, t2, t2); + + /* + * Compute x' = m^2 - 2*s. + */ + square_f256(Q->x, t1); + sub_f256(Q->x, Q->x, t2); + sub_f256(Q->x, Q->x, t2); + + /* + * Compute z' = 2*y*z. + */ + mul_f256(t4, Q->y, Q->z); + add_f256(Q->z, t4, t4); + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + sub_f256(t2, t2, Q->x); + mul_f256(Q->y, t1, t2); + square_f256(t4, t3); + add_f256(t4, t4, t4); + sub_f256(Q->y, Q->y, t4); +} + +/* + * Add point P2 to point P1. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 but P2 != 0 + * - If P1 != 0 but P2 == 0 + * - If P1 == P2 + * + * In all three cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - P1 == 0 and P2 == 0 + * - The Y coordinate of one of the points is 0 and the other point is + * the point at infinity. + * + * The third case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 and P2 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add(struct p256_jacobian *P1, const struct p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + */ + uint32_t t1[9], t2[9], t3[9], t4[9], t5[9], t6[9], t7[9]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + square_f256(t3, P2->z); + mul_f256(t1, P1->x, t3); + mul_f256(t4, P2->z, t3); + mul_f256(t3, P1->y, t4); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + sub_f256(t2, t2, t1); + sub_f256(t4, t4, t3); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 9; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + sub_f256(P1->x, P1->x, t5); + sub_f256(P1->x, P1->x, t6); + sub_f256(P1->x, P1->x, t6); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + sub_f256(t6, t6, P1->x); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + sub_f256(P1->y, P1->y, t1); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(t1, P1->z, P2->z); + mul_f256(P1->z, t1, t2); + + return ret; +} + +/* + * Add point P2 to point P1. This is a specialised function for the + * case when P2 is a non-zero point in affine coordinate. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 + * - If P1 == P2 + * + * In both cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - The Y coordinate of P2 is 0 and P1 is the point at infinity. + * + * The second case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add_mixed(struct p256_jacobian *P1, const struct p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 + * u2 = x2 * z1^2 + * s1 = y1 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 + */ + uint32_t t1[9], t2[9], t3[9], t4[9], t5[9], t6[9], t7[9]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1 (in t1) and s1 = y1 (in t3). + */ + memcpy(t1, P1->x, sizeof t1); + memcpy(t3, P1->y, sizeof t3); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + sub_f256(t2, t2, t1); + sub_f256(t4, t4, t3); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 9; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + sub_f256(P1->x, P1->x, t5); + sub_f256(P1->x, P1->x, t6); + sub_f256(P1->x, P1->x, t6); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + sub_f256(t6, t6, P1->x); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + sub_f256(P1->y, P1->y, t1); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(P1->z, P1->z, t2); + + return ret; +} + +/* + * Decode a P-256 point. This function does not support the point at + * infinity. Returned value is 0 if the point is invalid, 1 otherwise. + */ +static uint32_t +p256_decode(struct p256_jacobian *P, const void *src, size_t len) +{ + const unsigned char *buf; + uint32_t tx[9], ty[9], t1[9], t2[9]; + uint32_t bad; + int i; + + if (len != 65) { + return 0; + } + buf = (const unsigned char *)src; + + /* + * First byte must be 0x04 (uncompressed format). We could support + * "hybrid format" (first byte is 0x06 or 0x07, and encodes the + * least significant bit of the Y coordinate), but it is explicitly + * forbidden by RFC 5480 (section 2.2). + */ + bad = MG_BR_NEQ(buf[0], 0x04); + + /* + * Decode the coordinates, and check that they are both lower + * than the modulus. + */ + tx[8] = be8_to_le30(tx, buf + 1, 32); + ty[8] = be8_to_le30(ty, buf + 33, 32); + bad |= reduce_final_f256(tx); + bad |= reduce_final_f256(ty); + + /* + * Check curve equation. + */ + square_f256(t1, tx); + mul_f256(t1, tx, t1); + square_f256(t2, ty); + sub_f256(t1, t1, tx); + sub_f256(t1, t1, tx); + sub_f256(t1, t1, tx); + add_f256(t1, t1, P256_B); + sub_f256(t1, t1, t2); + reduce_final_f256(t1); + for (i = 0; i < 9; i ++) { + bad |= t1[i]; + } + + /* + * Copy coordinates to the point structure. + */ + memcpy(P->x, tx, sizeof tx); + memcpy(P->y, ty, sizeof ty); + memset(P->z, 0, sizeof P->z); + P->z[0] = 1; + return MG_BR_EQ(bad, 0); +} + +/* + * Encode a point into a buffer. This function assumes that the point is + * valid, in affine coordinates, and not the point at infinity. + */ +static void +p256_encode(void *dst, const struct p256_jacobian *P) +{ + unsigned char *buf; + + buf = (unsigned char *)dst; + buf[0] = 0x04; + le30_to_be8(buf + 1, 32, P->x); + le30_to_be8(buf + 33, 32, P->y); +} + +/* + * Multiply a curve point by an integer. The integer is assumed to be + * lower than the curve order, and the base point must not be the point + * at infinity. + */ +static void +p256_mul(struct p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 2-bit window to handle multiplier bits by pairs. + * The precomputed window really is the points P2 and P3. + */ + uint32_t qz; + struct p256_jacobian P2, P3, Q, T, U; + + /* + * Compute window values. + */ + P2 = *P; + p256_double(&P2); + P3 = *P; + p256_add(&P3, &P2); + + /* + * We start with Q = 0. We process multiplier bits 2 by 2. + */ + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + p256_double(&Q); + p256_double(&Q); + T = *P; + U = Q; + bits = (*x >> k) & (uint32_t)3; + bnz = MG_BR_NEQ(bits, 0); + MG_BR_CCOPY(MG_BR_EQ(bits, 2), &T, &P2, sizeof T); + MG_BR_CCOPY(MG_BR_EQ(bits, 3), &T, &P3, sizeof T); + p256_add(&U, &T); + MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q); + MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + *P = Q; +} + +/* + * Precomputed window: k*G points, where G is the curve generator, and k + * is an integer from 1 to 15 (inclusive). The X and Y coordinates of + * the point are encoded as 9 words of 30 bits each (little-endian + * order). + */ +static const uint32_t Gwin[15][18] = { + + { 0x1898C296, 0x1284E517, 0x1EB33A0F, 0x00DF604B, + 0x2440F277, 0x339B958E, 0x04247F8B, 0x347CB84B, + 0x00006B17, 0x37BF51F5, 0x2ED901A0, 0x3315ECEC, + 0x338CD5DA, 0x0F9E162B, 0x1FAD29F0, 0x27F9B8EE, + 0x10B8BF86, 0x00004FE3 }, + + { 0x07669978, 0x182D23F1, 0x3F21B35A, 0x225A789D, + 0x351AC3C0, 0x08E00C12, 0x34F7E8A5, 0x1EC62340, + 0x00007CF2, 0x227873D1, 0x3812DE74, 0x0E982299, + 0x1F6B798F, 0x3430DBBA, 0x366B1A7D, 0x2D040293, + 0x154436E3, 0x00000777 }, + + { 0x06E7FD6C, 0x2D05986F, 0x3ADA985F, 0x31ADC87B, + 0x0BF165E6, 0x1FBE5475, 0x30A44C8F, 0x3934698C, + 0x00005ECB, 0x227D5032, 0x29E6C49E, 0x04FB83D9, + 0x0AAC0D8E, 0x24A2ECD8, 0x2C1B3869, 0x0FF7E374, + 0x19031266, 0x00008734 }, + + { 0x2B030852, 0x024C0911, 0x05596EF5, 0x07F8B6DE, + 0x262BD003, 0x3779967B, 0x08FBBA02, 0x128D4CB4, + 0x0000E253, 0x184ED8C6, 0x310B08FC, 0x30EE0055, + 0x3F25B0FC, 0x062D764E, 0x3FB97F6A, 0x33CC719D, + 0x15D69318, 0x0000E0F1 }, + + { 0x03D033ED, 0x05552837, 0x35BE5242, 0x2320BF47, + 0x268FDFEF, 0x13215821, 0x140D2D78, 0x02DE9454, + 0x00005159, 0x3DA16DA4, 0x0742ED13, 0x0D80888D, + 0x004BC035, 0x0A79260D, 0x06FCDAFE, 0x2727D8AE, + 0x1F6A2412, 0x0000E0C1 }, + + { 0x3C2291A9, 0x1AC2ABA4, 0x3B215B4C, 0x131D037A, + 0x17DDE302, 0x0C90B2E2, 0x0602C92D, 0x05CA9DA9, + 0x0000B01A, 0x0FC77FE2, 0x35F1214E, 0x07E16BDF, + 0x003DDC07, 0x2703791C, 0x3038B7EE, 0x3DAD56FE, + 0x041D0C8D, 0x0000E85C }, + + { 0x3187B2A3, 0x0018A1C0, 0x00FEF5B3, 0x3E7E2E2A, + 0x01FB607E, 0x2CC199F0, 0x37B4625B, 0x0EDBE82F, + 0x00008E53, 0x01F400B4, 0x15786A1B, 0x3041B21C, + 0x31CD8CF2, 0x35900053, 0x1A7E0E9B, 0x318366D0, + 0x076F780C, 0x000073EB }, + + { 0x1B6FB393, 0x13767707, 0x3CE97DBB, 0x348E2603, + 0x354CADC1, 0x09D0B4EA, 0x1B053404, 0x1DE76FBA, + 0x000062D9, 0x0F09957E, 0x295029A8, 0x3E76A78D, + 0x3B547DAE, 0x27CEE0A2, 0x0575DC45, 0x1D8244FF, + 0x332F647A, 0x0000AD5A }, + + { 0x10949EE0, 0x1E7A292E, 0x06DF8B3D, 0x02B2E30B, + 0x31F8729E, 0x24E35475, 0x30B71878, 0x35EDBFB7, + 0x0000EA68, 0x0DD048FA, 0x21688929, 0x0DE823FE, + 0x1C53FAA9, 0x0EA0C84D, 0x052A592A, 0x1FCE7870, + 0x11325CB2, 0x00002A27 }, + + { 0x04C5723F, 0x30D81A50, 0x048306E4, 0x329B11C7, + 0x223FB545, 0x085347A8, 0x2993E591, 0x1B5ACA8E, + 0x0000CEF6, 0x04AF0773, 0x28D2EEA9, 0x2751EEEC, + 0x037B4A7F, 0x3B4C1059, 0x08F37674, 0x2AE906E1, + 0x18A88A6A, 0x00008786 }, + + { 0x34BC21D1, 0x0CCE474D, 0x15048BF4, 0x1D0BB409, + 0x021CDA16, 0x20DE76C3, 0x34C59063, 0x04EDE20E, + 0x00003ED1, 0x282A3740, 0x0BE3BBF3, 0x29889DAE, + 0x03413697, 0x34C68A09, 0x210EBE93, 0x0C8A224C, + 0x0826B331, 0x00009099 }, + + { 0x0624E3C4, 0x140317BA, 0x2F82C99D, 0x260C0A2C, + 0x25D55179, 0x194DCC83, 0x3D95E462, 0x356F6A05, + 0x0000741D, 0x0D4481D3, 0x2657FC8B, 0x1BA5CA71, + 0x3AE44B0D, 0x07B1548E, 0x0E0D5522, 0x05FDC567, + 0x2D1AA70E, 0x00000770 }, + + { 0x06072C01, 0x23857675, 0x1EAD58A9, 0x0B8A12D9, + 0x1EE2FC79, 0x0177CB61, 0x0495A618, 0x20DEB82B, + 0x0000177C, 0x2FC7BFD8, 0x310EEF8B, 0x1FB4DF39, + 0x3B8530E8, 0x0F4E7226, 0x0246B6D0, 0x2A558A24, + 0x163353AF, 0x000063BB }, + + { 0x24D2920B, 0x1C249DCC, 0x2069C5E5, 0x09AB2F9E, + 0x36DF3CF1, 0x1991FD0C, 0x062B97A7, 0x1E80070E, + 0x000054E7, 0x20D0B375, 0x2E9F20BD, 0x35090081, + 0x1C7A9DDC, 0x22E7C371, 0x087E3016, 0x03175421, + 0x3C6ECA7D, 0x0000F599 }, + + { 0x259B9D5F, 0x0D9A318F, 0x23A0EF16, 0x00EBE4B7, + 0x088265AE, 0x2CDE2666, 0x2BAE7ADF, 0x1371A5C6, + 0x0000F045, 0x0D034F36, 0x1F967378, 0x1B5FA3F4, + 0x0EC8739D, 0x1643E62A, 0x1653947E, 0x22D1F4E6, + 0x0FB8D64B, 0x0000B5B9 } +}; + +/* + * Lookup one of the Gwin[] values, by index. This is constant-time. + */ +static void +lookup_Gwin(struct p256_jacobian *T, uint32_t idx) +{ + uint32_t xy[18]; + uint32_t k; + size_t u; + + memset(xy, 0, sizeof xy); + for (k = 0; k < 15; k ++) { + uint32_t m; + + m = -MG_BR_EQ(idx, k + 1); + for (u = 0; u < 18; u ++) { + xy[u] |= m & Gwin[k][u]; + } + } + memcpy(T->x, &xy[0], sizeof T->x); + memcpy(T->y, &xy[9], sizeof T->y); + memset(T->z, 0, sizeof T->z); + T->z[0] = 1; +} + +/* + * Multiply the generator by an integer. The integer is assumed non-zero + * and lower than the curve order. + */ +static void +p256_mulgen(struct p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 4-bit window to handle multiplier bits by groups + * of 4. The precomputed window is constant static data, with + * points in affine coordinates; we use a constant-time lookup. + */ + struct p256_jacobian Q; + uint32_t qz; + + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + unsigned bx; + + bx = *x ++; + for (k = 0; k < 2; k ++) { + uint32_t bits; + uint32_t bnz; + struct p256_jacobian T, U; + + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + bits = (bx >> 4) & 0x0F; + bnz = MG_BR_NEQ(bits, 0); + lookup_Gwin(&T, bits); + U = Q; + p256_add_mixed(&U, &T); + MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q); + MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + bx <<= 4; + } + } + *P = Q; +} + +static const unsigned char P256_G[] = { + 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, + 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, + 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, + 0x98, 0xC2, 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, + 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, + 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, + 0x68, 0x37, 0xBF, 0x51, 0xF5 +}; + +static const unsigned char P256_N[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, + 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, + 0x25, 0x51 +}; + +static const unsigned char * +p256_api_generator(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P256_G; + return P256_G; +} + +static const unsigned char * +p256_api_order(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P256_N; + return P256_N; +} + +static size_t +p256_api_xoff(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return 1; +} + +static uint32_t +p256_api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + struct p256_jacobian P; + + (void)curve; + if (Glen != 65) { + return 0; + } + r = p256_decode(&P, G, Glen); + p256_mul(&P, x, xlen); + p256_to_affine(&P); + p256_encode(G, &P); + return r; +} + +static size_t +p256_api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + struct p256_jacobian P; + + (void)curve; + p256_mulgen(&P, x, xlen); + p256_to_affine(&P); + p256_encode(R, &P); + return 65; +} + +static uint32_t +p256_api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + struct p256_jacobian P, Q; + uint32_t r, t, z; + int i; + + (void)curve; + if (len != 65) { + return 0; + } + r = p256_decode(&P, A, len); + p256_mul(&P, x, xlen); + if (B == NULL) { + p256_mulgen(&Q, y, ylen); + } else { + r &= p256_decode(&Q, B, len); + p256_mul(&Q, y, ylen); + } + + /* + * The final addition may fail in case both points are equal. + */ + t = p256_add(&P, &Q); + reduce_final_f256(P.z); + z = 0; + for (i = 0; i < 9; i ++) { + z |= P.z[i]; + } + z = MG_BR_EQ(z, 0); + p256_double(&Q); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + MG_BR_CCOPY(z & ~t, &P, &Q, sizeof Q); + p256_to_affine(&P); + p256_encode(A, &P); + r &= ~(z & t); + return r; +} + +static const struct mg_br_ec_impl mg_br_ec_p256_m31 = { + (uint32_t)0x00800000, + &p256_api_generator, + &p256_api_order, + &p256_api_xoff, + &p256_api_mul, + &p256_api_mulgen, + &p256_api_muladd +}; + +#undef ARSH +#undef ARSHW + +// mg_p256_*() for MG_P256_BEAR, see tls_p256.h +int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]) { + const unsigned char *n; + unsigned char q[65]; + uint32_t x[I31_LEN], nn[I31_LEN]; + size_t nlen; + int tries, ok = 0; + n = mg_br_ec_p256_m31.order(MG_BR_EC_secp256r1, &nlen); + mg_br_i31_decode(nn, n, nlen); + for (tries = 0; tries < 64 && !ok; tries++) { // Private key in [1, n-1] + if (!mg_random(private_key, 32)) break; + ok = mg_br_i31_decode_mod(x, private_key, 32, nn) && !mg_br_i31_iszero(x); + } + if (ok) { + mg_br_ec_p256_m31.mulgen(q, private_key, 32, MG_BR_EC_secp256r1); + memcpy(public_key, q + 1, 64); + } + mg_bzero((unsigned char *) x, sizeof(x)); + return ok; +} + +int mg_p256_compute_public_key(const uint8_t private_key[32], + uint8_t public_key[64]) { + const unsigned char *n; + unsigned char q[65]; + uint32_t x[I31_LEN], nn[I31_LEN]; + size_t nlen; + int ok; + n = mg_br_ec_p256_m31.order(MG_BR_EC_secp256r1, &nlen); + mg_br_i31_decode(nn, n, nlen); + ok = mg_br_i31_decode_mod(x, private_key, 32, nn) && !mg_br_i31_iszero(x); + if (ok) { + mg_br_ec_p256_m31.mulgen(q, private_key, 32, MG_BR_EC_secp256r1); + memcpy(public_key, q + 1, 64); + } + mg_bzero((unsigned char *) x, sizeof(x)); + return ok; +} + +int mg_p256_shared_secret(const uint8_t public_key[64], + const uint8_t private_key[32], uint8_t secret[32]) { + unsigned char q[65]; + uint32_t ok; + q[0] = 0x04; + memcpy(q + 1, public_key, 64); + ok = mg_br_ec_p256_m31.mul(q, sizeof(q), private_key, 32, + MG_BR_EC_secp256r1); // Validates the point + memcpy(secret, q + 1, 32); + mg_bzero(q, sizeof(q)); + return ok == 1; +} + +int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, const uint8_t k[32], + uint8_t signature[64]) { + return mg_br_ecdsa_i31_sign_raw(&mg_br_ec_p256_m31, MG_BR_EC_secp256r1, + hash, hash_size, private_key, 32, k, + signature) == 64; +} + +int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash, + size_t hash_size, const uint8_t signature[64]) { + unsigned char q[65]; + q[0] = 0x04; + memcpy(q + 1, public_key, 64); + return mg_br_ecdsa_i31_vrfy_raw(&mg_br_ec_p256_m31, MG_BR_EC_secp256r1, hash, + hash_size, q, sizeof(q), signature, 64) == 1; +} +#endif // MG_P256 == MG_P256_BEAR + +// P-256 group order n, big endian +static const uint8_t s_p256_n[32] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, + 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51}; + +// Compute d = a - n, return 1 if a < n (borrow). Constant time +static uint32_t p256_sub_n(uint8_t d[32], const uint8_t a[32]) { + uint32_t borrow = 0, v; + int i; + for (i = 31; i >= 0; i--) { + v = (uint32_t) a[i] - s_p256_n[i] - borrow; + d[i] = (uint8_t) v; + borrow = (v >> 8) & 1; + } + return borrow; +} + +// ECDSA with deterministic k, RFC 6979 section 3.2, HMAC-SHA256. For P-256, +// qlen == hlen == 256 bits, so bits2int() is just the leftmost 32 bytes +int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, uint8_t signature[64]) { + uint8_t v[32], k[32], buf[32 + 1 + 32 + 32], h1[32], t[32]; + uint32_t lt, nz; + int i, tries, ok = 0; + + // h1 = bits2octets(hash): leftmost 256 bits, reduced modulo n + memset(h1, 0, sizeof(h1)); + if (hash_size >= 32) { + memcpy(h1, hash, 32); + } else { + memcpy(h1 + 32 - hash_size, hash, hash_size); + } + lt = p256_sub_n(t, h1); + for (i = 0; i < 32; i++) h1[i] = (uint8_t) MG_BR_MUX(lt, h1[i], t[i]); + + // Steps b-g: V = 0x01..., K = 0x00..., two HMAC rounds with x and h1 + memset(v, 1, sizeof(v)); + memset(k, 0, sizeof(k)); + memcpy(buf + 33, private_key, 32); + memcpy(buf + 65, h1, 32); + for (i = 0; i < 2; i++) { + memcpy(buf, v, 32); + buf[32] = (uint8_t) i; + mg_hmac_sha256(k, k, 32, buf, sizeof(buf)); + mg_hmac_sha256(v, k, 32, v, 32); + } + + // Step h: candidate k = V, must be in [1, n-1] + for (tries = 0; tries < 64 && !ok; tries++) { + mg_hmac_sha256(v, k, 32, v, 32); + for (nz = 0, i = 0; i < 32; i++) nz |= v[i]; + lt = p256_sub_n(t, v); + if (nz != 0 && lt) { + ok = mg_p256_sign_with_k(private_key, hash, hash_size, v, signature); + } + if (!ok) { // K = HMAC_K(V || 0x00), V = HMAC_K(V) + memcpy(buf, v, 32); + buf[32] = 0; + mg_hmac_sha256(k, k, 32, buf, 33); + mg_hmac_sha256(v, k, 32, v, 32); + } + } + mg_bzero(v, sizeof(v)); + mg_bzero(k, sizeof(k)); + mg_bzero(buf, sizeof(buf)); + mg_bzero(t, sizeof(t)); + return ok; +} + +#undef I31_LEN +#undef POINT_LEN +#undef ORDER_LEN +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + +#ifdef MG_ENABLE_LINES +#line 1 "src/tls_p256_cortex.c" +#endif +// P-256 for Cortex-M4/M7/M33, https://github.com/Emill/P256-Cortex-M4 +// commit ecd3ec2 +// Enabled by MG_P256=MG_P256_LENNGREN. Provides mg_p256_*(), see tls_p256.h +// +// Copyright (c) 2017-2021 Emil Lenngren +// Copyright (c) 2021 Shortcut Labs AB +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: the library's C code, header, config and GCC assembly are +// merged into one file, the assembly is a top-level __asm__ without .end. +// Trimmed with unifdef to keygen, ECDH, sign and verify. Uses the no-FPU field +// multiplication and the constant RAM access pattern table lookup (safe with a +// data cache). Internal C functions are static, casts added in slide_257(). +// mg_p256_*() wrappers are at the end + + + + +#if MG_P256 == MG_P256_LENNGREN && \ + (MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)) + +typedef const uint32_t (*constarr)[8]; + +struct FGInteger { + // To get the value this struct represents, + // interpret signed_value as a two's complement 288-bit little endian integer, + // and negate if flip_sign is -1 + int flip_sign; // 0 or -1 + uint32_t signed_value[9]; // of 288 bits, 257 are useful (top 31 bits are sign-extended from bit 256) +}; + +struct SignPrecomp { // p256_sign_step1() result + uint32_t r[8]; + uint32_t k_inv[8]; +}; + +struct XYInteger { + // To get the value this struct represents, + // interpret signed_value as an unsigned 288-bit little endian integer, + // and negate if flip_sign is -1 + int flip_sign; // 0 or -1 + uint32_t value[8]; // unsigned value, 0 <= value < P256_order +}; + +int P256_divsteps2_31(int delta, uint32_t f, uint32_t g, uint32_t res_matrix[4]); +void P256_matrix_mul_fg_9(uint32_t a, uint32_t b, const struct FGInteger fg[2], struct FGInteger *res); +void P256_matrix_mul_mod_n(uint32_t a, uint32_t b, const struct XYInteger xy[2], struct XYInteger *res); + +void P256_to_montgomery(uint32_t aR[8], const uint32_t a[8]); +void P256_from_montgomery(uint32_t a[8], const uint32_t aR[8]); +bool P256_check_range_p(const uint32_t a[8]); + +bool P256_check_range_n(const uint32_t a[8]); +void P256_mul_mod_n(uint32_t res[8], const uint32_t a[8], const uint32_t b[8]); +void P256_add_mod_n(uint32_t res[8], const uint32_t a[8], const uint32_t b[8]); +void P256_reduce_mod_n_32bytes(uint32_t res[8], const uint32_t a[8]); + +void P256_select_point(uint32_t (*output)[8], uint32_t* table, uint32_t num_coordinates, uint32_t index); + +void P256_jacobian_to_affine(uint32_t affine_mont_x[8], uint32_t affine_mont_y[8], const uint32_t jacobian_mont[3][8]); +bool P256_point_is_on_curve(const uint32_t x_mont[8], const uint32_t y_mont[8]); +void P256_double_j(uint32_t jacobian_point_out[3][8], const uint32_t jacobian_point_in[3][8]); +void P256_add_sub_j(uint32_t jacobian_point1[3][8], const uint32_t (*point2)[8], bool is_sub, bool p2_is_affine); +bool P256_verify_last_step(const uint32_t r[8], const uint32_t jacobian_point[3][8]); + +void P256_negate_mod_p_if(uint32_t out[8], const uint32_t in[8], uint32_t should_negate); +void P256_negate_mod_n_if(uint32_t out[8], const uint32_t in[8], uint32_t should_negate); + +extern uint32_t P256_order[9]; + +static const uint32_t one_montgomery[8] = {1, 0, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0}; + +// This table contains 1G, 3G, 5G, ... 15G in affine coordinates in montgomery form +static const uint32_t p256_basepoint_precomp[8][2][8] = { +{{0x18a9143c, 0x79e730d4, 0x5fedb601, 0x75ba95fc, 0x77622510, 0x79fb732b, 0xa53755c6, 0x18905f76}, +{0xce95560a, 0xddf25357, 0xba19e45c, 0x8b4ab8e4, 0xdd21f325, 0xd2e88688, 0x25885d85, 0x8571ff18}}, +{{0x4eebc127, 0xffac3f90, 0x87d81fb, 0xb027f84a, 0x87cbbc98, 0x66ad77dd, 0xb6ff747e, 0x26936a3f}, +{0xc983a7eb, 0xb04c5c1f, 0x861fe1a, 0x583e47ad, 0x1a2ee98e, 0x78820831, 0xe587cc07, 0xd5f06a29}}, +{{0xc45c61f5, 0xbe1b8aae, 0x94b9537d, 0x90ec649a, 0xd076c20c, 0x941cb5aa, 0x890523c8, 0xc9079605}, +{0xe7ba4f10, 0xeb309b4a, 0xe5eb882b, 0x73c568ef, 0x7e7a1f68, 0x3540a987, 0x2dd1e916, 0x73a076bb}}, +{{0xa0173b4f, 0x746354e, 0xd23c00f7, 0x2bd20213, 0xc23bb08, 0xf43eaab5, 0xc3123e03, 0x13ba5119}, +{0x3f5b9d4d, 0x2847d030, 0x5da67bdd, 0x6742f2f2, 0x77c94195, 0xef933bdc, 0x6e240867, 0xeaedd915}}, +{{0x264e20e8, 0x75c96e8f, 0x59a7a841, 0xabe6bfed, 0x44c8eb00, 0x2cc09c04, 0xf0c4e16b, 0xe05b3080}, +{0xa45f3314, 0x1eb7777a, 0xce5d45e3, 0x56af7bed, 0x88b12f1a, 0x2b6e019a, 0xfd835f9b, 0x86659cd}}, +{{0x6245e404, 0xea7d260a, 0x6e7fdfe0, 0x9de40795, 0x8dac1ab5, 0x1ff3a415, 0x649c9073, 0x3e7090f1}, +{0x2b944e88, 0x1a768561, 0xe57f61c8, 0x250f939e, 0x1ead643d, 0xc0daa89, 0xe125b88e, 0x68930023}}, +{{0x4b2ed709, 0xccc42563, 0x856fd30d, 0xe356769, 0x559e9811, 0xbcbcd43f, 0x5395b759, 0x738477ac}, +{0xc00ee17f, 0x35752b90, 0x742ed2e3, 0x68748390, 0xbd1f5bc1, 0x7cd06422, 0xc9e7b797, 0xfbc08769}}, +{{0xbc60055b, 0x72bcd8b7, 0x56e27e4b, 0x3cc23ee, 0xe4819370, 0xee337424, 0xad3da09, 0xe2aa0e43}, +{0x6383c45d, 0x40b8524f, 0x42a41b25, 0xd7663554, 0x778a4797, 0x64efa6de, 0x7079adf4, 0x2042170a}} +}; + +// This contains two tables, 8 points each in affine coordinates in montgomery form +// The first table contains these points: +// (2^192 - 2^128 - 2^64 - 1)G +// (2^192 - 2^128 - 2^64 + 1)G +// (2^192 - 2^128 + 2^64 - 1)G +// (2^192 - 2^128 + 2^64 + 1)G +// (2^192 + 2^128 - 2^64 - 1)G +// (2^192 + 2^128 - 2^64 + 1)G +// (2^192 + 2^128 + 2^64 - 1)G +// (2^192 + 2^128 + 2^64 + 1)G +// The second table contains the same points multiplied by 2^32 +static const uint32_t p256_basepoint_precomp2[2][8][2][8] = +{ +{ +{{0x670844e0, 0x52d8a7c9, 0xef68a29d, 0xe33bdc, 0x4bdb7361, 0xf3d2848, 0x91c5304d, 0x5222c821}, +{0xdf73fc25, 0xea6d2944, 0x255c81b, 0xa04c0f55, 0xefe488a8, 0x29acdc97, 0x80a560de, 0xbe2e158f}}, +{{0x2b13e673, 0xfc8511ee, 0xd103ed24, 0xffc58dee, 0xea7e99b8, 0x1022523a, 0x4afc8a17, 0x8f43ea39}, +{0xc5f33d0b, 0x8f4e2dbc, 0xd0aa1681, 0x3bc099fa, 0x79ff9df1, 0xffbb7b41, 0xd58b57c4, 0x180de09d}}, +{{0x8bd1cda5, 0x56430752, 0x8e05eda5, 0x1807577f, 0x956896e9, 0x99c699b, 0xf1f0efb5, 0x83d6093d}, +{0xed97061c, 0xef5af17e, 0x30d4c3c, 0x35b977b8, 0x49229439, 0x81fa75a2, 0xa0b6d35d, 0xf5a22070}}, +{{0x74f81cf1, 0x814c5365, 0x120065b, 0xe30baff7, 0x15132621, 0x80ae1256, 0x36a80788, 0x16d2b8cb}, +{0xecc50bca, 0x33d14697, 0x17aedd21, 0x19a9dfb0, 0xedc3f766, 0x523fbcc7, 0xb2cf5afd, 0x9c4de6dd}}, +{{0xcf0d9f6d, 0x5305a9e6, 0x81a9b021, 0x5839172f, 0x75c687cf, 0xcca7a4dd, 0x844be22f, 0x36d59b3e}, +{0x111a53e9, 0xcace7e62, 0xf063f3a1, 0x91c843d4, 0xda812da, 0xbf77e5f0, 0x437f3176, 0xe64af9c}}, +{{0xcf07517d, 0xdbd568bb, 0xba6830b9, 0x2f1afba2, 0xe6c4c2a6, 0x15b6807c, 0xe4966aef, 0x91c7eabc}, +{0xd6b2b6e6, 0x716dea1b, 0x19f85b4b, 0x248c43d1, 0x4a315e2a, 0x16dcfd60, 0xc72b3d0b, 0x15fdd303}}, +{{0x42b7dfd5, 0xe40bf9f4, 0x2d934f2a, 0x673689f3, 0x30a6f50b, 0x8314beb4, 0x976ec64e, 0xd17af2bc}, +{0x1ee7ddf1, 0x39f66c4f, 0x68ea373c, 0x7f68e18b, 0x53d0b186, 0x5166c1f2, 0x7be58f14, 0x95dda601}}, +{{0x42913074, 0xd5ae356, 0x48a542b1, 0x55491b27, 0xb310732a, 0x469ca665, 0x5f1a4cc1, 0x29591d52}, +{0xb84f983f, 0xe76f5b6b, 0x9f5f84e1, 0xbe7eef41, 0x80baa189, 0x1200d496, 0x18ef332c, 0x6376551f}} +}, +{ +{{0x7c4e54f5, 0xb9e5cbc0, 0xe1410e34, 0xc53a1a17, 0xec454425, 0x3e199130, 0x1700902e, 0xb029c97e}, +{0x786423b6, 0x2de66e11, 0xb41a95be, 0x262dc914, 0x451b683, 0x51766abd, 0x85bb6fb1, 0x55ad5f34}}, +{{0x9066cb79, 0x74f4f1c, 0x30c8b94e, 0x1ab31bd6, 0xd74275b3, 0x6d3f012f, 0x9ddcce40, 0xa214d0b1}, +{0xd165050a, 0x24aedf74, 0xe0e5dc3e, 0x95f17ece, 0xd9224456, 0x6ada9cda, 0x2dd60eea, 0x1fadb2d1}}, +{{0xe20cfb9b, 0xa3d83091, 0xba76e0cb, 0xae79c975, 0xc8858a6e, 0xa5f2a588, 0x874a3168, 0xe897a5f4}, +{0x7d48f096, 0xf6c1ef40, 0xc35b132c, 0x1f9c516b, 0x53c479fd, 0xe1040f91, 0x9df06743, 0x60e881f}}, +{{0x52a90e51, 0x9e0ad72, 0x38c50a96, 0xb7e66ea3, 0x7d997770, 0xab32ad05, 0x445671cb, 0xceaffe2}, +{0x5d37cc99, 0xdfbe753c, 0xe0fea2d5, 0x95d068cc, 0x4dd77cb6, 0x1e37cdda, 0x55530688, 0x88c5a4bb}}, +{{0xc7744f1, 0x3413f033, 0xbc816702, 0x23c05c89, 0x1192b5ac, 0x2322ee9a, 0x373180bb, 0xc1636a0}, +{0xbdde0207, 0xfe2f3d4, 0xc23578d8, 0xe1a093a, 0xc888ead, 0x6e5f0d1, 0x52a2b660, 0x9ca285a5}}, +{{0xce923964, 0xdae76995, 0xa34c7993, 0xcc96493a, 0xea73d9e7, 0xd19b5144, 0x311e6e34, 0x4a5c263}, +{0xd9a2a443, 0x7db5b32b, 0x2cfd960c, 0x3754bd33, 0xa430f15, 0xc5bcc98, 0xd9a94574, 0x5651201f}}, +{{0xfc0418fe, 0xebdd8921, 0x34e20036, 0x37015b39, 0xdf03a353, 0xcf4fcd8f, 0xf12cab16, 0xdc2de6e1}, +{0xd071df14, 0x9c17cc1a, 0x63415530, 0xd7c5e6a3, 0x68f3fb1e, 0xb5301660, 0x18269301, 0xb5f70bc9}}, +{{0x79ec1a0f, 0x2d8daefd, 0xceb39c97, 0x3bbcd6fd, 0x58f61a95, 0xf5575ffc, 0xadf7b420, 0xdbd986c4}, +{0x15f39eb7, 0x81aa8814, 0xb98d976c, 0x6ee2fcf5, 0xcf2f717d, 0x5465475d, 0x6860bbd0, 0x8e24d3c4}} +} +}; + +// Takes the leftmost 256 bits in hash (treated as big endian), +// and converts to little endian integer z. +static void hash_to_z(uint32_t z[8], const uint8_t* hash, uint32_t hashlen) { + if (hashlen > 32) { + hashlen = 32; + } + for (uint32_t i = 0; i < hashlen; i++) { + ((uint8_t*)z)[i] = hash[hashlen - 1 - i]; + } + for (uint32_t i = hashlen; i < 32; i++) { + ((uint8_t*)z)[i] = 0; + } +} + +// Creates a representation of a (little endian integer), +// so that r[0] + 2*r[1] + 2^2*r[2] + 2^3*r[3] + ... = a, +// where each r[i] is -15, -13, ..., 11, 13, 15 or 0. +// Only around 1/5.5 of the r[i] will be non-zero. +static void slide_257(signed char r[257], const uint8_t a[32]) { + for (int i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + r[256] = 0; + + for (int i = 0; i < 256; i++) { + if (r[i] != 0) { + for (int b = 1; b <= 4 && i + b < 256; b++) { + if (r[i + b] != 0) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] = (signed char) (r[i] + (r[i + b] << b)); r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] = (signed char) (r[i] - (r[i + b] << b)); + for (;;) { + r[i + b] = 0; + b++; + if (!r[i + b]) { + r[i + b] = 1; + b--; // Will be added back after loop footer b++ + break; + } + } + } else { + break; + } + } + } + } + } +} + +static void P256_mod_n_inv(uint32_t out[8], const uint32_t in[8]) { + // This function follows the algorithm in section 12.1 of https://gcd.cr.yp.to/safegcd-20190413.pdf. + // It has been altered in the following ways: + // 1. Due to 32-bit cpu, we use 24 * 31 iterations instead of 12 * 62. + // 2. P-256 modulus instead of 2^255-19. + // 744 iterations are still enough and slightly more than the required 741 (floor((49*256+57)/17)). + // 3. Step 5 has been corrected to go back to step 2 instead of step 3. + // 4. The order of the matrix multiplications in step 6 has been changed to (T24*(T23*(T22*(...*(T1*[0, 1]))))), + // where [0, 1] is a column vector to make it possible to be able to extract the "top-right corner", v, of T24*T23*...*T1. + // The result v will then be contained in the first element of the resulting column vector. + + struct { + struct FGInteger fg[2]; // f and g + struct XYInteger xy[2]; // x and y + } state[2]; // "current" and "next" + + state[0].fg[0].flip_sign = 0; // non-negative f + memcpy(&state[0].fg[0].signed_value, P256_order, 36); // f + state[0].fg[1].flip_sign = 0; // non-negative g + memcpy(&state[0].fg[1].signed_value, in, 32); // g + state[0].fg[1].signed_value[8] = 0; // upper bits of g are 0 + memset(&state[0].xy, 0, sizeof(state[0].xy)); + // We later need a factor 2^-744. The montgomery multiplication gives 2^(24*-32)=2^-768, so multiply the init value (1) by 2^24 here. + state[0].xy[1].value[0] = 1U << 24; + + int delta = 1; + for (int i = 0; i < 24; i++) { + // Scaled translation matrix Ti + uint32_t matrix[4]; // element range: [-2^30, 2^31] (negative numbers are stored in two's complement form) + + // Decode f and g into two's complement representation and use the lowest 32 bits in the P256_divsteps2_31 calculation + uint32_t negate_f = state[i % 2].fg[0].flip_sign; + uint32_t negate_g = state[i % 2].fg[1].flip_sign; + delta = P256_divsteps2_31(delta, (state[i % 2].fg[0].signed_value[0] ^ negate_f) - negate_f, (state[i % 2].fg[1].signed_value[0] ^ negate_g) - negate_g, matrix); + + // "Jump step", calculates the new f and g values that applies after 31 divstep2 iterations + P256_matrix_mul_fg_9(matrix[0], matrix[1], state[i % 2].fg, &state[(i + 1) % 2].fg[0]); + P256_matrix_mul_fg_9(matrix[2], matrix[3], state[i % 2].fg, &state[(i + 1) % 2].fg[1]); + + // Iterate the result vector + // Due to montgomery multiplication inside this function, each step also adds a 2^-32 factor + P256_matrix_mul_mod_n(matrix[0], matrix[1], state[i % 2].xy, &state[(i + 1) % 2].xy[0]); + P256_matrix_mul_mod_n(matrix[2], matrix[3], state[i % 2].xy, &state[(i + 1) % 2].xy[1]); + } + // Calculates val^-1 = sgn(f) * v * 2^-744, where v is the "top-right corner" of the resulting T24*T23*...*T1 matrix. + // In this implementation, at this point x contains v * 2^-744. + P256_negate_mod_n_if(out, &state[0].xy[0].value[0], (state[0].xy[0].flip_sign ^ state[0].fg[0].flip_sign ^ state[0].fg[0].signed_value[8]) & 1); +} + +// Constant time abs +static inline uint32_t abs_int(int8_t a) { + uint32_t a_u = (uint32_t)(int32_t)a; + uint32_t mask = a_u >> 31; + mask |= mask << 1; + mask |= mask << 2; + uint32_t result = (-a) & mask; + result |= a & (mask ^ 0xf); + return result; +} + +// Calculates scalar*P in constant time (except for the scalars 2 and n-2, for which the results take a few extra cycles to compute) +static void scalarmult_variable_base(uint32_t output_mont_x[8], uint32_t output_mont_y[8], const uint32_t input_mont_x[8], const uint32_t input_mont_y[8], const uint32_t scalar[8]) { + // Based on https://eprint.iacr.org/2014/130.pdf, Algorithm 1. + + uint32_t scalar2[8]; + int8_t e[64]; + + // The algorithm used requires the scalar to be odd. If even, negate the scalar modulo p to make it odd, and later negate the end result. + bool even = (scalar[0] & 1) ^ 1; + P256_negate_mod_n_if(scalar2, scalar, even); + + // Rewrite the scalar as e[0] + 2^4*e[1] + 2^8*e[2] + ... + 2^252*e[63], where each e[i] is an odd number and -15 <= e[i] <= 15. + e[0] = scalar2[0] & 0xf; + for (int i = 1; i < 64; i++) { + // Extract 4 bits + e[i] = (scalar2[i / 8] >> ((i % 8) * 4)) & 0xf; + // If even, subtract 2^4 from e[i - 1] and add 1 to e[i] + e[i - 1] -= (int8_t) (((e[i] & 1) ^ 1) << 4); + e[i] |= 1; + } + + // Create a table of P, 3P, 5P, ... 15P. + uint32_t table[8][3][8]; + memcpy(table[0][0], input_mont_x, 32); + memcpy(table[0][1], input_mont_y, 32); + memcpy(table[0][2], one_montgomery, 32); + P256_double_j(table[7], (constarr)table[0]); + for (int i = 1; i < 8; i++) { + memcpy(table[i], table[7], 96); + P256_add_sub_j(table[i], (constarr)table[i - 1], 0, 0); + } + + // Calculate the result as (((((((((e[63]*G)*2^4)+e[62])*2^4)+e[61])*2^4)...)+e[1])*2^4)+e[0] = (2^252*e[63] + 2^248*e[62] + ... + e[0])*G. + + uint32_t current_point[3][8]; + + // e[63] is never negative + P256_select_point(current_point, (uint32_t*)table, 3, e[63] >> 1); + + for (uint32_t i = 63; i --> 0;) { + for (int j = 3; j >= 0; j--) { + P256_double_j(current_point, (constarr)current_point); + } + uint32_t selected_point[3][8]; + P256_select_point(selected_point, (uint32_t*)table, 3, abs_int(e[i]) >> 1); + P256_negate_mod_p_if(selected_point[1], selected_point[1], (uint8_t)e[i] >> 7); + + // There is (only) one odd input scalar that leads to an exception when i == 0: n-2, + // in that case current_point will be equal to selected_point and hence a doubling + // will occur instead. We don't bother fixing the same constant time for that case since + // the probability of that random value to be generated is around 1/2^255 and an + // attacker could easily test this case anyway. + P256_add_sub_j(current_point, (constarr)selected_point, false, false); + } + P256_jacobian_to_affine(output_mont_x, output_mont_y, (constarr)current_point); + + // If the scalar was initially even, we now negate the result to get the correct result, since -(scalar*G) = (-scalar*G). + // This is done by negating y, since -(x,y) = (x,-y). + P256_negate_mod_p_if(output_mont_y, output_mont_y, even); +} + +#define get_bit(arr, i) ((arr[(i) / 32] >> ((i) % 32)) & 1) + +// Calculates scalar*G in constant time +static void scalarmult_fixed_base(uint32_t output_mont_x[8], uint32_t output_mont_y[8], const uint32_t scalar[8]) { + uint32_t scalar2[8]; + + // Just as with the algorithm used in variable base scalar multiplication, this algorithm requires the scalar to be odd. + bool even = (scalar[0] & 1) ^ 1; + P256_negate_mod_n_if(scalar2, scalar, even); + + // This algorithm conceptually rewrites the odd scalar as s[0] + 2^1*s[1] + 2^2*s[2] + ... + 2^255*s[255], where each s[i] is -1 or 1. + // By initially setting s[i] to the corresponding bit S[i] in the original odd scalar S, we go from lsb to msb, and whenever a value s[i] is 0, + // increase s[i] by 1 and decrease s[i-1] by 2. + // This will result in that s[i] = S[i+1] == 1 ? 1 : -1 for i < 255, and s[255] = 1. + + // We then form the scalars abs(s[j] + s[j+64]*2^64 + s[j+128]*2^128 + s[j+192]*2^192)*(2^32 * floor(j / 32)) for different 0 <= j < 64. + // Each scalar times G has already been precomputed in p256_basepoint_precomp2. + // That way we only need 31 point doublings and 63 point additions. + + uint32_t current_point[3][8]; + uint32_t selected_point[2][8]; + + for (uint32_t i = 32; i --> 0;) { + { + uint32_t mask = get_bit(scalar2, i + 32 + 1) | (get_bit(scalar2, i + 64 + 32 + 1) << 1) | (get_bit(scalar2, i + 2 * 64 + 32 + 1) << 2); + if (i == 31) { + P256_select_point(current_point, (uint32_t*)p256_basepoint_precomp2[1], 2, mask); + memcpy(current_point[2], one_montgomery, 32); + } else { + P256_double_j(current_point, (constarr)current_point); + + uint32_t sign = get_bit(scalar2, i + 3 * 64 + 32 + 1) - 1; // positive: 0, negative: -1 + mask = (mask ^ sign) & 7; + P256_select_point(selected_point, (uint32_t*)p256_basepoint_precomp2[1], 2, mask); + P256_negate_mod_p_if(selected_point[1], selected_point[1], sign & 1); + P256_add_sub_j(current_point, (constarr)selected_point, false, true); + } + } + { + uint32_t mask = get_bit(scalar2, i + 1) | (get_bit(scalar2, i + 64 + 1) << 1) | (get_bit(scalar2, i + 2 * 64 + 1) << 2); + uint32_t sign = get_bit(scalar2, i + 3 * 64 + 1) - 1; // positive: 0, negative: -1 + mask = (mask ^ sign) & 7; + P256_select_point(selected_point, (uint32_t*)p256_basepoint_precomp2[0], 2, mask); + P256_negate_mod_p_if(selected_point[1], selected_point[1], sign & 1); + P256_add_sub_j(current_point, (constarr)selected_point, false, true); + } + } + P256_jacobian_to_affine(output_mont_x, output_mont_y, (constarr)current_point); + + // Negate final result if the scalar was initially even. + P256_negate_mod_p_if(output_mont_y, output_mont_y, even); +} + +static void p256_convert_endianness(void* output, const void* input, size_t byte_len) { + for (size_t i = 0; i < byte_len / 2; i++) { + uint8_t t = ((uint8_t*)input)[byte_len - 1 - i]; + ((uint8_t*)output)[byte_len - 1 - i] = ((uint8_t*)input)[i]; + ((uint8_t*)output)[i] = t; + } +} + +static bool p256_verify(const uint32_t public_key_x[8], const uint32_t public_key_y[8], const uint8_t* hash, uint32_t hashlen_in_bytes, const uint32_t r[8], const uint32_t s[8]) { + if (!P256_check_range_n(r) || !P256_check_range_n(s)) { + return false; + } + + if (!P256_check_range_p(public_key_x) || !P256_check_range_p(public_key_y)) { + return false; + } + + uint32_t pk_table[8][3][8]; + P256_to_montgomery(pk_table[0][0], public_key_x); + P256_to_montgomery(pk_table[0][1], public_key_y); + memcpy(pk_table[0][2], one_montgomery, 32); + + if (!P256_point_is_on_curve(pk_table[0][0], pk_table[0][1])) { + return false; + } + + // Create a table of P, 3P, 5P, ..., 15P, where P is the public key. + P256_double_j(pk_table[7], (constarr)pk_table[0]); + for (int i = 1; i < 8; i++) { + memcpy(pk_table[i], pk_table[7], 96); + P256_add_sub_j(pk_table[i], (constarr)pk_table[i - 1], 0, 0); + } + + uint32_t z[8], w[8], u1[8], u2[8]; + + hash_to_z(z, hash, hashlen_in_bytes); + + P256_mod_n_inv(w, s); + + P256_mul_mod_n(u1, z, w); + P256_mul_mod_n(u2, r, w); + + // Each value in these arrays will be an odd integer v, so that -15 <= v <= 15. + // Around 1/5.5 of them will be non-zero. + signed char slide_bp[257], slide_pk[257]; + slide_257(slide_bp, (uint8_t*)u1); + slide_257(slide_pk, (uint8_t*)u2); + + uint32_t cp[3][8] = {0}; + + for (int i = 256; i >= 0; i--) { + P256_double_j(cp, (constarr)cp); + if (slide_bp[i] > 0) { + P256_add_sub_j(cp, p256_basepoint_precomp[slide_bp[i]/2], 0, 1); + } else if (slide_bp[i] < 0) { + P256_add_sub_j(cp, p256_basepoint_precomp[(-slide_bp[i])/2], 1, 1); + } + if (slide_pk[i] > 0) { + P256_add_sub_j(cp, (constarr)pk_table[slide_pk[i]/2], 0, 0); + } else if (slide_pk[i] < 0) { + P256_add_sub_j(cp, (constarr)pk_table[(-slide_pk[i])/2], 1, 0); + } + } + + return P256_verify_last_step(r, (constarr)cp); +} + +static bool p256_sign_step1(struct SignPrecomp *result, const uint32_t k[8]) { + do { + uint32_t point_res[2][8]; + if (!P256_check_range_n(k)) { + break; + } + scalarmult_fixed_base(point_res[0], point_res[1], k); + P256_mod_n_inv(result->k_inv, k); + P256_from_montgomery(result->r, point_res[0]); + P256_reduce_mod_n_32bytes(result->r, result->r); + + uint32_t r_sum = 0; + for (int i = 0; i < 8; i++) { + r_sum |= result->r[i]; + } + if (r_sum == 0) { + break; + } + return true; + } while (false); + + memset(result, 0, sizeof(struct SignPrecomp)); + return false; +} + +static bool p256_sign_step2(uint32_t r[8], uint32_t s[8], const uint8_t* hash, uint32_t hashlen_in_bytes, const uint32_t private_key[8], struct SignPrecomp *sign_precomp) { + do { + if (!P256_check_range_n(sign_precomp->k_inv) || !P256_check_range_n(sign_precomp->r)) { // just make sure user did not input an obviously invalid precomp + break; + } + uint32_t *const z = r; + hash_to_z(z, hash, hashlen_in_bytes); + P256_mul_mod_n(s, sign_precomp->r, private_key); + P256_add_mod_n(s, z, s); + P256_mul_mod_n(s, sign_precomp->k_inv, s); + + memcpy(r, sign_precomp->r, 32); + + uint32_t s_sum = 0; + for (int i = 0; i < 8; i++) { + s_sum |= s[i]; + } + if (s_sum == 0) { + break; + } + memset(sign_precomp, 0, sizeof(*sign_precomp)); + return true; + } while (false); + + memset(r, 0, 32); + memset(s, 0, 32); + return false; +} + +static bool p256_sign(uint32_t r[8], uint32_t s[8], const uint8_t* hash, uint32_t hashlen_in_bytes, const uint32_t private_key[8], const uint32_t k[8]) { + struct SignPrecomp t; + if (!p256_sign_step1(&t, k)) { + memset(r, 0, 32); + memset(s, 0, 32); + return false; + } + return p256_sign_step2(r, s, hash, hashlen_in_bytes, private_key, &t); +} + +static bool p256_scalarmult_base(uint32_t result_x[8], uint32_t result_y[8], const uint32_t scalar[8]) { + if (!P256_check_range_n(scalar)) { + return false; + } + scalarmult_fixed_base(result_x, result_y, scalar); + P256_from_montgomery(result_x, result_x); + P256_from_montgomery(result_y, result_y); + return true; + +} + +static bool p256_keygen(uint32_t public_key_x[8], uint32_t public_key_y[8], const uint32_t private_key[8]) { + return p256_scalarmult_base(public_key_x, public_key_y, private_key); +} + + +static bool p256_scalarmult_generic_no_scalar_check(uint32_t output_mont_x[8], uint32_t output_mont_y[8], const uint32_t scalar[8], const uint32_t in_x[8], const uint32_t in_y[8]) { + if (!P256_check_range_p(in_x) || !P256_check_range_p(in_y)) { + return false; + } + + P256_to_montgomery(output_mont_x, in_x); + P256_to_montgomery(output_mont_y, in_y); + + if (!P256_point_is_on_curve(output_mont_x, output_mont_y)) { + return false; + } + + scalarmult_variable_base(output_mont_x, output_mont_y, output_mont_x, output_mont_y, scalar); + return true; +} + +static bool p256_ecdh_calc_shared_secret(uint8_t shared_secret[32], const uint32_t private_key[8], const uint32_t others_public_key_x[8], const uint32_t others_public_key_y[8]) { + uint32_t result_x[8], result_y[8]; + if (!p256_scalarmult_generic_no_scalar_check(result_x, result_y, private_key, others_public_key_x, others_public_key_y)) { + return false; + } + P256_from_montgomery(result_x, result_x); + p256_convert_endianness(shared_secret, result_x, 32); + return true; +} + +// Assembler, from p256-cortex-m4-asm-gcc.S, converted to a top-level asm +// block so it can live in the amalgamated mongoose.c +__asm__( + ".pushsection .text.mg_p256,\"ax\",%progbits\n" + "\t.syntax unified\n" + "\t.thumb\n" + "\t.align 2\n" + "\t.type P256_select_point, %function\n" + "P256_select_point:\n" + "\t.global P256_select_point\n" + "\tpush {r0,r2,r3,r4-r11,lr}\n" + "\tsubs r2,#1\n" + "\tlsls r2,#5\n" + "0:\n" + "\trsbs r3,#0\n" + "\tsbcs r3,r3,r3\n" + "\tmvns r3,r3\n" + "\tldm r1!,{r6-r12,lr}\n" + "\tands r6,r3\n" + "\tands r7,r3\n" + "\tand r8,r3\n" + "\tand r9,r3\n" + "\tand r10,r3\n" + "\tand r11,r3\n" + "\tand r12,r3\n" + "\tand lr,r3\n" + "\tadds r1,r2\n" + "\tmovs r3,#1\n" + "1:\n" + "\tldr r0,[sp,#8]\n" + "\teors r0,r3\n" + "\tmrs r0,apsr\n" + "\tlsrs r0,#30\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r6,r2,r0,r4\n" + "\tumlal r7,r3,r0,r5\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r8,r2,r0,r4\n" + "\tumlal r9,r3,r0,r5\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r10,r2,r0,r4\n" + "\tumlal r11,r3,r0,r5\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r12,r2,r0,r4\n" + "\tumlal lr,r3,r0,r5\n" + "\tadds r1,r2\n" + "\tadds r3,#1\n" + "\tcmp r3,#8\n" + "\tbne 1b\n" + "\tldm sp,{r0,r4}\n" + "\tstm r0!,{r6-r12,lr}\n" + "\tstr r0,[sp]\n" + "\tsub r1,r1,r2, lsl #3\n" + "\tsubs r1,#224\n" + "\tsubs r4,#1\n" + "\tstr r4,[sp,#4]\n" + "\tldr r3,[sp,#8]\n" + "\tbne 0b\n" + "\tadd sp,#12\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_select_point, .-P256_select_point\n" + "\t.type mul288x288, %function\n" + "mul288x288:\n" + "\tpush {r4-r11,lr}\n" + "\tmov r4,r0\n" + "\tmov r5,r2\n" + "\tmov r6,r1\n" + "\tmovs r1,#72\n" + "\tbl setzero\n" + "\tldm r5,{r0-r2,r8-r12,lr}\n" + "\tmovs r7,#9\n" + "0:\n" + "\tldm r6!,{r5}\n" + "\tpush {r6,r7}\n" + "\tmovs r3,#0\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r0\n" + "\tumaal r7,r3,r5,r1\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r2\n" + "\tumaal r7,r3,r5,r8\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r9\n" + "\tumaal r7,r3,r5,r10\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r11\n" + "\tumaal r7,r3,r5,r12\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6}\n" + "\tumaal r3,r6,r5,lr\n" + "\tstm r4!,{r3,r6}\n" + "\tsubs r4,r4,#36\n" + "\tpop {r6,r7}\n" + "\tsubs r7,r7,#1\n" + "\tbne 0b\n" + "\tpop {r4-r11,pc}\n" + "\t.size mul288x288, .-mul288x288\n" + "\t.type setzero, %function\n" + "setzero:\n" + "\tmovs r2,#0\n" + "\tmovs r3,#0\n" + "0:\n" + "\tstm r0!,{r2,r3}\n" + "\tsubs r1,r1,#8\n" + "\tbne 0b\n" + "\tbx lr\n" + "\t.size setzero, .-setzero\n" + "\t.type P256_mulmod, %function\n" + "P256_mulmod:\n" + "\tpush {r2,lr}\n" + "\tsub sp,#28\n" + "\tldm r2,{r2,r3,r4,r5}\n" + "\tldm r1!,{r0,r10,lr}\n" + "\tumull r6,r11,r2,r0\n" + "\tumull r7,r12,r3,r0\n" + "\tumaal r7,r11,r2,r10\n" + "\tpush {r6,r7}\n" + "\tumull r8,r6,r4,r0\n" + "\tumaal r8,r11,r3,r10\n" + "\tumull r9,r7,r5,r0\n" + "\tumaal r9,r11,r4,r10\n" + "\tumaal r11,r7,r5,r10\n" + "\tumaal r8,r12,r2,lr\n" + "\tumaal r9,r12,r3,lr\n" + "\tumaal r11,r12,r4,lr\n" + "\tumaal r12,r7,r5,lr\n" + "\tldm r1!,{r0,r10,lr}\n" + "\tumaal r9,r6,r2,r0\n" + "\tumaal r11,r6,r3,r0\n" + "\tumaal r12,r6,r4,r0\n" + "\tumaal r6,r7,r5,r0\n" + "\tstrd r8,r9,[sp,#8]\n" + "\tmov r9,#0\n" + "\tumaal r11,r9,r2,r10\n" + "\tumaal r12,r9,r3,r10\n" + "\tumaal r6,r9,r4,r10\n" + "\tumaal r7,r9,r5,r10\n" + "\tmov r10,#0\n" + "\tumaal r12,r10,r2,lr\n" + "\tumaal r6,r10,r3,lr\n" + "\tumaal r7,r10,r4,lr\n" + "\tumaal r9,r10,r5,lr\n" + "\tldr r8,[r1],#4\n" + "\tmov lr,#0\n" + "\tumaal lr,r6,r2,r8\n" + "\tumaal r7,r6,r3,r8\n" + "\tumaal r9,r6,r4,r8\n" + "\tumaal r10,r6,r5,r8\n" + "\tldr r8,[r1],#-28\n" + "\tmov r0,#0\n" + "\tumaal r7,r0,r2,r8\n" + "\tumaal r9,r0,r3,r8\n" + "\tumaal r10,r0,r4,r8\n" + "\tumaal r6,r0,r5,r8\n" + "\tpush {r0}\n" + "\tldr r2,[sp,#40]\n" + "\tadds r2,r2,#16\n" + "\tldm r2,{r2,r3,r4,r5}\n" + "\tldr r8,[r1],#4\n" + "\tmov r0,#0\n" + "\tumaal r11,r0,r2,r8\n" + "\tstr r11,[sp,#16+4]\n" + "\tumaal r12,r0,r3,r8\n" + "\tumaal lr,r0,r4,r8\n" + "\tumaal r0,r7,r5,r8\n" + "\tldr r8,[r1],#4\n" + "\tmov r11,#0\n" + "\tumaal r12,r11,r2,r8\n" + "\tstr r12,[sp,#20+4]\n" + "\tumaal lr,r11,r3,r8\n" + "\tumaal r0,r11,r4,r8\n" + "\tumaal r11,r7,r5,r8\n" + "\tldr r8,[r1],#4\n" + "\tmov r12,#0\n" + "\tumaal lr,r12,r2,r8\n" + "\tstr lr,[sp,#24+4]\n" + "\tumaal r0,r12,r3,r8\n" + "\tumaal r11,r12,r4,r8\n" + "\tumaal r10,r12,r5,r8\n" + "\tldr r8,[r1],#4\n" + "\tmov lr,#0\n" + "\tumaal r0,lr,r2,r8\n" + "\tstr r0,[sp,#28+4]\n" + "\tumaal r11,lr,r3,r8\n" + "\tumaal r10,lr,r4,r8\n" + "\tumaal r6,lr,r5,r8\n" + "\tldm r1!,{r0,r8}\n" + "\tumaal r11,r9,r2,r0\n" + "\tstr r11,[sp,#32+4]\n" + "\tumaal r9,r10,r3,r0\n" + "\tumaal r10,r6,r4,r0\n" + "\tpop {r11}\n" + "\tumaal r11,r6,r5,r0\n" + "\tumaal r9,r7,r2,r8\n" + "\tumaal r10,r7,r3,r8\n" + "\tumaal r11,r7,r4,r8\n" + "\tumaal r6,r7,r5,r8\n" + "\tldm r1!,{r0,r8}\n" + "\tumaal r10,r12,r2,r0\n" + "\tumaal r11,r12,r3,r0\n" + "\tumaal r6,r12,r4,r0\n" + "\tumaal r7,r12,r5,r0\n" + "\tumaal r11,lr,r2,r8\n" + "\tumaal lr,r6,r3,r8\n" + "\tumaal r6,r7,r4,r8\n" + "\tumaal r7,r12,r5,r8\n" + "\tpush {r6,r7,r12}\n" + "\tadd r7,sp,#12\n" + "\tldm r7,{r0-r8}\n" + "\tmov r12,#0\n" + "\tadds r3,r0\n" + "\tadcs r4,r4,r1\n" + "\tadcs r5,r5,r2\n" + "\tadcs r6,r6,r0\n" + "\tadcs r7,r7,r1\n" + "\tadcs r8,r8,r0\n" + "\tadcs r9,r9,r1\n" + "\tadcs r10,r10,#0\n" + "\tadcs r11,r11,#0\n" + "\tadcs r12,r12,#0\n" + "\tadds r6,r3\n" + "\tadcs r7,r7,r4\n" + "\tadcs r8,r8,r2\n" + "\tadcs r9,r9,r3\n" + "\tadcs r10,r10,r2\n" + "\tadcs r11,r11,r3\n" + "\tadcs r12,r12,#0\n" + "\tsubs r7,r0\n" + "\tsbcs r8,r8,r1\n" + "\tsbcs r9,r9,r2\n" + "\tsbcs r10,r10,r3\n" + "\tsbcs r11,r11,#0\n" + "\tsbcs r12,r12,#0\n" + "\tpop {r1-r3}\n" + "\tadds r0,lr,r12\n" + "\tadcs r1,r1,#0\n" + "\tmov r12,#0\n" + "\tadcs r12,r12,#0\n" + "\tadcs r8,r8,r5\n" + "\tadcs r9,r9,r6\n" + "\tadcs r10,r10,r4\n" + "\tadcs r11,r11,r5\n" + "\tadcs r0,r0,r4\n" + "\tadcs r1,r1,r5\n" + "\tadcs r2,r2,r12\n" + "\tadcs r3,r3,#0\n" + "\tmov r12,#0\n" + "\tadcs r12,r12,#0\n" + "\tadcs r10,r10,r7\n" + "\tadcs r11,r11,#0\n" + "\tadcs r0,r0,r6\n" + "\tadcs r1,r1,r7\n" + "\tadcs r2,r2,r6\n" + "\tadcs r3,r3,r7\n" + "\tadcs r12,r12,#0\n" + "\tsubs r11,r4\n" + "\tsbcs r0,r0,r5\n" + "\tsbcs r1,r1,r6\n" + "\tsbcs r2,r2,r7\n" + "\tsbcs r3,r3,#0\n" + "\tsbcs r12,r12,#0\n" + "\tsubs r8,r8,#0xffffffff\n" + "\tsbcs r9,r9,#0xffffffff\n" + "\tsbcs r10,r10,#0xffffffff\n" + "\tsbcs r11,r11,#0\n" + "\tsbcs r4,r0,#0\n" + "\tsbcs r5,r1,#0\n" + "\tsbcs r6,r2,#1\n" + "\tsbcs r7,r3,#0xffffffff\n" + "\tsbc r12,r12,#0\n" + "\tadds r0,r8,r12\n" + "\tadcs r1,r9,r12\n" + "\tadcs r2,r10,r12\n" + "\tadcs r3,r11,#0\n" + "\tadcs r4,r4,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r6,r12, lsr #31\n" + "\tadcs r7,r7,r12\n" + "\tadd sp,sp,#40\n" + "\tpop {pc}\n" + "\t.size P256_mulmod, .-P256_mulmod\n" + "\t.type P256_sqrmod, %function\n" + "P256_sqrmod:\n" + "\tpush {lr}\n" + "\tumull r9,r10,r0,r0\n" + "\tumull r11,r12,r0,r1\n" + "\tadds r11,r11,r11\n" + "\tmov lr,#0\n" + "\tumaal r10,r11,lr,lr\n" + "\tpush {r9,r10}\n" + "\tmov r9,#0\n" + "\tumaal r9,r12,r0,r2\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,r1,r1\n" + "\tpush {r9}\n" + "\tumull r9,r10,r0,r3\n" + "\tumaal r9,r12,r1,r2\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,lr,lr\n" + "\tpush {r9}\n" + "\tmov r9,#0\n" + "\tumaal r9,r10,r0,r4\n" + "\tumaal r9,r12,r1,r3\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,r2,r2\n" + "\tpush {r9}\n" + "\tumull r9,r8,r0,r5\n" + "\tumaal r9,r10,r1,r4\n" + "\tumaal r9,r12,r2,r3\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,lr,lr\n" + "\tpush {r9}\n" + "\tmov r9,#0\n" + "\tumaal r9,r8,r1,r5\n" + "\tumaal r9,r12,r2,r4\n" + "\tumaal r9,r10,r0,r6\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,r3,r3\n" + "\tpush {r9}\n" + "\tumull r9,r0,r0,r7\n" + "\tumaal r9,r10,r1,r6\n" + "\tumaal r9,r12,r2,r5\n" + "\tumaal r9,r8,r3,r4\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,lr,lr\n" + "\tumaal r0,r8,r1,r7\n" + "\tumaal r0,r10,r2,r6\n" + "\tumaal r0,r12,r3,r5\n" + "\tadcs r0,r0,r0\n" + "\tumaal r11,r0,r4,r4\n" + "\tumaal r12,r8,r2,r7\n" + "\tumaal r12,r10,r3,r6\n" + "\tmovs r2,#0\n" + "\tumaal r12,r2,r4,r5\n" + "\tadcs r1,r12,r12\n" + "\tumaal r0,r1,lr,lr\n" + "\tumaal r2,r8,r3,r7\n" + "\tumaal r2,r10,r4,r6\n" + "\tadcs r2,r2,r2\n" + "\tumaal r1,r2,r5,r5\n" + "\tmovs r3,#0\n" + "\tumaal r3,r8,r4,r7\n" + "\tumaal r3,r10,r5,r6\n" + "\tadcs r3,r3,r3\n" + "\tumaal r2,r3,lr,lr\n" + "\tumaal r8,r10,r5,r7\n" + "\tadcs r8,r8,r8\n" + "\tumaal r3,r8,r6,r6\n" + "\tumull r4,r5,lr,lr\n" + "\tumaal r4,r10,r6,r7\n" + "\tadcs r4,r4,r4\n" + "\tumaal r4,r8,lr,lr\n" + "\tadcs r10,r10,r10\n" + "\tumaal r8,r10,r7,r7\n" + "\tadcs r10,r10,lr\n" + "\tpush {r4,r8,r10}\n" + "\tadd r4,sp,#12\n" + "\tldm r4,{r4-r8,r10,r12}\n" + "\tX0 .req r10\n" + "\tX1 .req r12\n" + "\tX2 .req r8\n" + "\tX3 .req r7\n" + "\tX4 .req r6\n" + "\tX5 .req r5\n" + "\tX6 .req r4\n" + "\tX7 .req r9\n" + "\tX8 .req r11\n" + "\tX9 .req r0\n" + "\tX10 .req r1\n" + "\tX11 .req r2\n" + "\tX12 .req r3\n" + "\tX13 .req r7\n" + "\tX14 .req r8\n" + "\tX15 .req r12\n" + "\tadcs X3,X3,X0\n" + "\tadcs X4,X4,X1\n" + "\tadcs X5,X5,X2\n" + "\tadcs X6,X6,X0\n" + "\tadcs X7,X7,X1\n" + "\tadcs X8,X8,X0\n" + "\tadcs X9,X9,X1\n" + "\tadcs X10,X10,#0\n" + "\tadcs X11,X11,#0\n" + "\tadcs lr,lr,#0\n" + "\tadds X6,X3\n" + "\tadcs X7,X7,X4\n" + "\tadcs X8,X8,X2\n" + "\tadcs X9,X9,X3\n" + "\tadcs X10,X10,X2\n" + "\tadcs X11,X11,X3\n" + "\tadcs lr,lr,#0\n" + "\tsubs X7,X0\n" + "\tsbcs X8,X8,X1\n" + "\tsbcs X9,X9,X2\n" + "\tsbcs X10,X10,X3\n" + "\tsbcs X11,X11,#0\n" + "\tsbcs lr,lr,#0\n" + "\tpop {X13,X14,X15}\n" + "\tadds X0,X12,lr\n" + "\tadcs X13,X13,#0\n" + "\tmov lr,#0\n" + "\tadcs lr,lr,#0\n" + "\tadcs X8,X8,X5\n" + "\tadcs X9,X9,X6\n" + "\tadcs X10,X10,X4\n" + "\tadcs X11,X11,X5\n" + "\tadcs X0,X0,X4\n" + "\tadcs X13,X13,X5\n" + "\tadcs X14,X14,lr\n" + "\tadcs X15,X15,#0\n" + "\tmov lr,#0\n" + "\tadcs lr,lr,#0\n" + "\tadcs X10,X10,X7\n" + "\tadcs X11,X11,#0\n" + "\tadcs X0,X0,X6\n" + "\tadcs X13,X13,X7\n" + "\tadcs X14,X14,X6\n" + "\tadcs X15,X15,X7\n" + "\tadcs lr,lr,#0\n" + "\tsubs X11,X4\n" + "\tsbcs X0,X0,X5\n" + "\tsbcs X13,X13,X6\n" + "\tsbcs X14,X14,X7\n" + "\tsbcs X15,X15,#0\n" + "\tsbcs lr,lr,#0\n" + "\tsubs r11,r11,#0xffffffff\n" + "\tsbcs r9,r0,#0xffffffff\n" + "\tsbcs r4,r1,#0xffffffff\n" + "\tsbcs r3,r2,#0\n" + "\tsbcs r6,r10,#0\n" + "\tsbcs r5,r7,#0\n" + "\tsbcs r10,r8,#1\n" + "\tsbcs r8,r12,#0xffffffff\n" + "\tsbcs r7,lr,#0\n" + "\tadds r0,r11,r7\n" + "\tadcs r1,r9,r7\n" + "\tadcs r2,r4,r7\n" + "\tadcs r3,r3,#0\n" + "\tadcs r4,r6,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r10,r7, lsr #31\n" + "\tadcs r7,r8,r7\n" + "\tadd sp,#28\n" + "\tpop {pc}\n" + "\t.size P256_sqrmod, .-P256_sqrmod\n" + "\t.type P256_submod, %function\n" + "P256_submod:\n" + "\tldm r1,{r3-r10}\n" + "\tldm r2!,{r0,r1,r11,r12}\n" + "\tsubs r3,r0\n" + "\tsbcs r4,r4,r1\n" + "\tsbcs r5,r5,r11\n" + "\tsbcs r6,r6,r12\n" + "\tldm r2,{r0,r1,r11,r12}\n" + "\tsbcs r7,r7,r0\n" + "\tsbcs r8,r8,r1\n" + "\tsbcs r9,r9,r11\n" + "\tsbcs r10,r10,r12\n" + "\tsbcs r11,r11,r11\n" + "\tadds r0,r3,r11\n" + "\tadcs r1,r4,r11\n" + "\tadcs r2,r5,r11\n" + "\tadcs r3,r6,#0\n" + "\tadcs r4,r7,#0\n" + "\tadcs r5,r8,#0\n" + "\tadcs r6,r9,r11, lsr #31\n" + "\tadcs r7,r10,r11\n" + "\tbx lr\n" + "\t.size P256_submod, .-P256_submod\n" + "\t.type P256_addmod, %function\n" + "P256_addmod:\n" + "\tldm r2,{r2-r9}\n" + "\tldm r1!,{r0,r10,r11,r12}\n" + "\tadds r2,r0\n" + "\tadcs r3,r3,r10\n" + "\tadcs r4,r4,r11\n" + "\tadcs r5,r5,r12\n" + "\tldm r1,{r0,r1,r11,r12}\n" + "\tadcs r6,r6,r0\n" + "\tadcs r7,r7,r1\n" + "\tadcs r8,r8,r11\n" + "\tadcs r9,r9,r12\n" + "\tmovs r10,#0\n" + "\tadcs r10,r10,r10\n" + "\tsubs r2,#0xffffffff\n" + "\tsbcs r3,r3,#0xffffffff\n" + "\tsbcs r4,r4,#0xffffffff\n" + "\tsbcs r5,r5,#0\n" + "\tsbcs r6,r6,#0\n" + "\tsbcs r7,r7,#0\n" + "\tsbcs r8,r8,#1\n" + "\tsbcs r9,r9,#0xffffffff\n" + "\tsbcs r10,r10,#0\n" + "\tadds r0,r2,r10\n" + "\tadcs r1,r3,r10\n" + "\tadcs r2,r4,r10\n" + "\tadcs r3,r5,#0\n" + "\tadcs r4,r6,#0\n" + "\tadcs r5,r7,#0\n" + "\tadcs r6,r8,r10, lsr #31\n" + "\tadcs r7,r9,r10\n" + "\tbx lr\n" + "\t.size P256_addmod, .-P256_addmod\n" + "\t.type P256_sqrmod_many, %function\n" + "P256_sqrmod_many:\n" + "\tpush {r8,lr}\n" + "0:\n" + "\tbl P256_sqrmod\n" + "\tldr r8,[sp,#0]\n" + "\tsubs r8,r8,#1\n" + "\tstr r8,[sp,#0]\n" + "\tbne 0b\n" + "\tpop {r8,pc}\n" + "\t.size P256_sqrmod_many, .-P256_sqrmod_many\n" + "\t.type P256_sqrmod_many_and_mulmod, %function\n" + "P256_sqrmod_many_and_mulmod:\n" + "\tpush {r9,lr}\n" + "\tbl P256_sqrmod_many\n" + "\tpush {r0-r7}\n" + "\tmov r1,sp\n" + "\tldr r2,[sp,#32]\n" + "\tbl P256_mulmod\n" + "\tadd sp,#36\n" + "\tpop {pc}\n" + "\t.size P256_sqrmod_many_and_mulmod, .-P256_sqrmod_many_and_mulmod\n" + "\t.type P256_modinv_sqrt, %function\n" + "P256_modinv_sqrt:\n" + "\tpush {r0-r8,lr}\n" + "\tmov r8,#1\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tmov r1,sp\n" + "\tadd r2,sp,#32\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmov r8,#8-4\n" + "\tadd r9,sp,#32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tmov r8,#16-8\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tmov r8,#16\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tmov r8,#32\n" + "\tadd r9,sp,#5*32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tldr r8,[sp,#6*32]\n" + "\tcmp r8,#0\n" + "\tbne 0f\n" + "\tmov r8,#192-64\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#224-192\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#240-224\n" + "\tadd r9,sp,#32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#248-240\n" + "\tadd r9,sp,#64\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#252-248\n" + "\tadd r9,sp,#128\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#256-252\n" + "\tadd r9,sp,#96\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tmov r1,sp\n" + "\tadd r2,sp,#5*32\n" + "\tbl P256_mulmod\n" + "\tb 1f\n" + "0:\n" + "\tmov r8,#160-64\n" + "\tadd r9,sp,#5*32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#254-160\n" + "\tbl P256_sqrmod_many\n" + "1:\n" + "\tadd sp,#6*32+4\n" + "\tpop {pc}\n" + "\t.size P256_modinv_sqrt, .-P256_modinv_sqrt\n" + "\t.type P256_times2, %function\n" + "P256_times2:\n" + "\tadds r0,r0\n" + "\tadcs r1,r1,r1\n" + "\tadcs r2,r2,r2\n" + "\tadcs r3,r3,r3\n" + "\tadcs r4,r4,r4\n" + "\tadcs r5,r5,r5\n" + "\tadcs r6,r6,r6\n" + "\tadcs r7,r7,r7\n" + "\tmov r8,#0\n" + "\tadcs r8,r8,r8\n" + "\tsubs r0,#0xffffffff\n" + "\tsbcs r1,r1,#0xffffffff\n" + "\tsbcs r2,r2,#0xffffffff\n" + "\tsbcs r3,r3,#0\n" + "\tsbcs r4,r4,#0\n" + "\tsbcs r5,r5,#0\n" + "\tsbcs r6,r6,#1\n" + "\tsbcs r7,r7,#0xffffffff\n" + "\tsbcs r8,r8,#0\n" + "\tadds r0,r8\n" + "\tadcs r1,r1,r8\n" + "\tadcs r2,r2,r8\n" + "\tadcs r3,r3,#0\n" + "\tadcs r4,r4,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r6,r8, lsr #31\n" + "\tadcs r7,r7,r8\n" + "\tbx lr\n" + "\t.size P256_times2, .-P256_times2\n" + "\t.align 2\n" + "R2_mod_p:\n" + "\t.word 3\n" + "\t.word 0\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffb\n" + "\t.word 0xfffffffe\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffd\n" + "\t.word 4\n" + "\t.type P256_to_montgomery, %function\n" + "P256_to_montgomery:\n" + "\t.global P256_to_montgomery\n" + "\tpush {r0,r4-r11,lr}\n" + "\tadr r2,R2_mod_p\n" + "\tbl P256_mulmod\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_to_montgomery, .-P256_to_montgomery\n" + "\t.type P256_from_montgomery, %function\n" + "P256_from_montgomery:\n" + "\t.global P256_from_montgomery\n" + "\tpush {r0,r4-r11,lr}\n" + "\tmovs r2,#0\n" + "\tmovs r3,#0\n" + "\tpush {r2-r3}\n" + "\tpush {r2-r3}\n" + "\tpush {r2-r3}\n" + "\tmovs r2,#1\n" + "\tpush {r2-r3}\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd sp,#32\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_from_montgomery, .-P256_from_montgomery\n" + "\t.type P256_check_range_p, %function\n" + "P256_check_range_p:\n" + "\t.global P256_check_range_p\n" + "\tpush {r4-r8,lr}\n" + "\tldm r0,{r1-r8}\n" + "\tmovs r0,#0xffffffff\n" + "\tsubs r1,r0\n" + "\tsbcs r2,r2,r0\n" + "\tsbcs r3,r3,r0\n" + "\tsbcs r4,r4,#0\n" + "\tsbcs r5,r5,#0\n" + "\tsbcs r6,r6,#0\n" + "\tsbcs r7,r7,#1\n" + "\tsbcs r8,r8,r0\n" + "\tsbcs r0,r0,r0\n" + "\tlsrs r0,#31\n" + "\tpop {r4-r8,pc}\n" + "\t.size P256_check_range_p, .-P256_check_range_p\n" + "\t.align 2\n" + "P256_order_mu:\n" + "\t.word 0xeedf9bfe\n" + "\t.word 0x012ffd85\n" + "\t.word 0xdf1a6c21\n" + "\t.word 0x43190552\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffe\n" + "\t.word 0xffffffff\n" + "\t.word 0x0\n" + "\t.word 0x1\n" + "\t.type P256_reduce_mod_n_once, %function\n" + "P256_reduce_mod_n_once:\n" + "\tpush {lr}\n" + "\tadr r10,P256_order\n" + "\tldm r10,{r10,r11,r12,lr}\n" + "\tsubs r0,r10\n" + "\tsbcs r1,r1,r11\n" + "\tsbcs r2,r2,r12\n" + "\tsbcs r3,r3,lr\n" + "\tsbcs r4,r4,#0xffffffff\n" + "\tsbcs r5,r5,#0xffffffff\n" + "\tsbcs r6,r6,#0\n" + "\tsbcs r7,r7,#0xffffffff\n" + "\tsbcs r8,r8,#0\n" + "\tsbc r9,r9,r9\n" + "\tand r10,r9\n" + "\tand r11,r9\n" + "\tand r12,r9\n" + "\tand lr,r9\n" + "\tadds r0,r10\n" + "\tadcs r1,r1,r11\n" + "\tadcs r2,r2,r12\n" + "\tadcs r3,r3,lr\n" + "\tadcs r4,r4,r9\n" + "\tadcs r5,r5,r9\n" + "\tadcs r6,r6,#0\n" + "\tadcs r7,r7,r9\n" + "\tadcs r8,r8,#0\n" + "\tpop {pc}\n" + "\t.size P256_reduce_mod_n_once, .-P256_reduce_mod_n_once\n" + "\t.type P256_reduce_mod_n_64bytes, %function\n" + "P256_reduce_mod_n_64bytes:\n" + "\tpush {r0,r4-r11,lr}\n" + "\tsub sp,sp,#108\n" + "\tmov r10,r1\n" + "\tadd r0,sp,#36\n" + "\tadds r1,r1,#28\n" + "\tadr r2,P256_order_mu\n" + "\tbl mul288x288\n" + "\tmov r0,sp\n" + "\tadd r1,sp,#72\n" + "\tadr r2,P256_order\n" + "\tbl mul288x288\n" + "\tldm r10,{r0-r8}\n" + "\tpop {r9-r12}\n" + "\tsubs r0,r0,r9\n" + "\tsbcs r1,r1,r10\n" + "\tsbcs r2,r2,r11\n" + "\tsbcs r3,r3,r12\n" + "\tpop {r9-r12,lr}\n" + "\tsbcs r4,r4,r9\n" + "\tsbcs r5,r5,r10\n" + "\tsbcs r6,r6,r11\n" + "\tsbcs r7,r7,r12\n" + "\tsbcs r8,r8,lr\n" + "\tbl P256_reduce_mod_n_once\n" + "\tbl P256_reduce_mod_n_once\n" + "\tadd sp,sp,#72\n" + "\tpop {r9}\n" + "\tstm r9,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_reduce_mod_n_64bytes, .-P256_reduce_mod_n_64bytes\n" + "\t.type P256_reduce_mod_n_32bytes, %function\n" + "P256_reduce_mod_n_32bytes:\n" + "\t.global P256_reduce_mod_n_32bytes\n" + "\tpush {r0,r4-r11,lr}\n" + "\tldm r1,{r0-r7}\n" + "\tmov r8,#0\n" + "\tbl P256_reduce_mod_n_once\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_reduce_mod_n_32bytes, .-P256_reduce_mod_n_32bytes\n" + "\t.type P256_add_mod_n, %function\n" + "P256_add_mod_n:\n" + "\t.global P256_add_mod_n\n" + "\tpush {r0,r4-r11,lr}\n" + "\tmov r12,r1\n" + "\tldm r2,{r4-r11}\n" + "\tldm r12!,{r0-r3}\n" + "\tadds r0,r4\n" + "\tadcs r1,r1,r5\n" + "\tadcs r2,r2,r6\n" + "\tadcs r3,r3,r7\n" + "\tldm r12,{r4-r7}\n" + "\tadcs r4,r4,r8\n" + "\tadcs r5,r5,r9\n" + "\tadcs r6,r6,r10\n" + "\tadcs r7,r7,r11\n" + "\tmovs r8,#0\n" + "\tadcs r8,r8,r8\n" + "\tbl P256_reduce_mod_n_once\n" + "\tbl P256_reduce_mod_n_once\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_add_mod_n, .-P256_add_mod_n\n" + "\t.type P256_mul_mod_n, %function\n" + "P256_mul_mod_n:\n" + "\t.global P256_mul_mod_n\n" + "\tmovs r3,#0\n" + "\tpush {r3-r10,lr}\n" + "\tmov r4,r0\n" + "\tldm r1,{r1,r3,r5-r10}\n" + "\tpush {r1,r3,r5-r10}\n" + "\tmovs r1,#0\n" + "\tpush {r1}\n" + "\tldm r2,{r1,r3,r5-r10}\n" + "\tpush {r1,r3,r5-r10}\n" + "\tsub sp,#72\n" + "\tmov r0,sp\n" + "\tadd r1,sp,#72\n" + "\tadd r2,sp,#108\n" + "\tbl mul288x288\n" + "\tmov r0,r4\n" + "\tmov r1,sp\n" + "\tbl P256_reduce_mod_n_64bytes\n" + "\tadd sp,#144\n" + "\tpop {r4-r10,pc}\n" + "\t.size P256_mul_mod_n, .-P256_mul_mod_n\n" + "\t.type P256_divsteps2_31, %function\n" + "P256_divsteps2_31:\n" + "\t.global P256_divsteps2_31\n" + "\tpush {r3,r4-r8,lr}\n" + "\tmovs r4,#1\n" + "\tmovs r5,#0\n" + "\tmovs r6,#0\n" + "\tmovs r7,#1\n" + "\tmov lr,#31\n" + "0:\n" + "\tsubs r3,r0,#1\n" + "\tlsl r12,r2,#31\n" + "\tbic r3,r12,r3\n" + "\tasrs r3,r3,#31\n" + "\tlsr r8,r3,#31\n" + "\teors r0,r0,r3\n" + "\tsubs r0,r0,r3\n" + "\tmul r12,r1,r3\n" + "\tbics r1,r1,r3\n" + "\tumlal r1,r12,r2,r8\n" + "\tumaal r2,r12,r2,r3\n" + "\tmul r12,r4,r3\n" + "\tbics r4,r4,r3\n" + "\tumlal r4,r12,r6,r8\n" + "\tumaal r6,r12,r6,r3\n" + "\tmul r12,r5,r3\n" + "\tbics r5,r5,r3\n" + "\tumlal r5,r12,r7,r8\n" + "\tumaal r7,r12,r7,r3\n" + "\tands r12,r2,#1\n" + "\tadds r0,r0,#1\n" + "\tmul r3,r12,r1\n" + "\tadds r2,r2,r3\n" + "\tlsrs r2,r2,#1\n" + "\tumlal r6,r8,r12,r4\n" + "\tumlal r7,r8,r12,r5\n" + "\tadds r4,r4,r4\n" + "\tadds r5,r5,r5\n" + "\tsubs lr,lr,#1\n" + "\tbne 0b\n" + "\tpop {r3}\n" + "\tstm r3!,{r4-r7}\n" + "\tpop {r4-r8,pc}\n" + "\t.size P256_divsteps2_31, .-P256_divsteps2_31\n" + "\t.type P256_matrix_mul_fg_9, %function\n" + "P256_matrix_mul_fg_9:\n" + "\t.global P256_matrix_mul_fg_9\n" + "\tpush {r4-r11,lr}\n" + "\tand r4,r0,r0,lsl #1\n" + "\tasrs r4,r4,#31\n" + "\teors r0,r0,r4\n" + "\tsubs r0,r0,r4\n" + "\tand r5,r1,r1,lsl #1\n" + "\tasrs r5,r5,#31\n" + "\teors r1,r1,r5\n" + "\tsubs r1,r1,r5\n" + "\tldm r2!,{r6}\n" + "\tldr r7,[r2,#36]\n" + "\teors r4,r4,r6\n" + "\teors r5,r5,r7\n" + "\teors r4,r4,r5\n" + "\tstm r3!,{r5}\n" + "\tpush {r1,r2,r3}\n" + "\tldm r2!,{r1,r3,r5-r11}\n" + "\teors r1,r1,r4\n" + "\teors r3,r3,r4\n" + "\teors r5,r5,r4\n" + "\teors r6,r6,r4\n" + "\teors r7,r7,r4\n" + "\teor r8,r8,r4\n" + "\teor r9,r9,r4\n" + "\teor r10,r10,r4\n" + "\tsubs r1,r1,r4\n" + "\tsbcs r3,r3,r4\n" + "\tsbcs r5,r5,r4\n" + "\tsbcs r6,r6,r4\n" + "\tsbcs r7,r7,r4\n" + "\tsbcs r8,r8,r4\n" + "\tsbcs r9,r9,r4\n" + "\tsbcs r10,r10,r4\n" + "\teor r4,r4,r11\n" + "\tumull r1,lr,r0,r1\n" + "\tmovs r2,#0\n" + "\tumull r11,r12,r2,r2\n" + "\tumaal r2,lr,r0,r3\n" + "\tumaal r11,lr,r0,r5\n" + "\tumull r3,r5,r12,r12\n" + "\tumaal r3,lr,r0,r6\n" + "\tumaal r5,lr,r0,r7\n" + "\tumull r6,r7,r12,r12\n" + "\tumaal r6,lr,r0,r8\n" + "\tumaal r7,lr,r0,r9\n" + "\tumaal r12,lr,r0,r10\n" + "\tmla lr,r0,r4,lr\n" + "\tpop {r0,r4}\n" + "\tadds r4,r4,#40\n" + "\tldm r4!,{r8,r9}\n" + "\tmov r10,#0\n" + "\tumaal r1,r10,r0,r8\n" + "\tumaal r2,r10,r0,r9\n" + "\tadds r1,r1,r1\n" + "\tadcs r2,r2,r2\n" + "\tldm r4!,{r1,r8,r9}\n" + "\tumaal r10,r11,r0,r1\n" + "\tumaal r11,r3,r0,r8\n" + "\tumaal r3,r5,r0,r9\n" + "\tadcs r10,r10,r10\n" + "\tadcs r11,r11,r11\n" + "\tadcs r3,r3,r3\n" + "\tldm r4,{r1,r4,r8,r9}\n" + "\tumaal r5,r6,r0,r1\n" + "\tumaal r6,r7,r0,r4\n" + "\tumaal r7,r12,r0,r8\n" + "\tumaal r12,lr,r0,r9\n" + "\tadcs r5,r5,r5\n" + "\tadcs r6,r6,r6\n" + "\tadcs r7,r7,r7\n" + "\tadcs r12,r12,r12\n" + "\tsbcs lr,lr,lr\n" + "\tmvn lr,lr\n" + "\tpop {r1}\n" + "\tstm r1!,{r2,r10,r11}\n" + "\tstm r1!,{r3,r5,r6,r7,r12,lr}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_matrix_mul_fg_9, .-P256_matrix_mul_fg_9\n" + "\t.align 2\n" + "\t.type P256_matrix_mul_mod_n, %function\n" + "P256_matrix_mul_mod_n:\n" + "\t.global P256_matrix_mul_mod_n\n" + "\tpush {r4-r11,lr}\n" + "\tand r4,r0,r0,lsl #1\n" + "\tasrs r4,r4,#31\n" + "\teors r0,r0,r4\n" + "\tsubs r0,r0,r4\n" + "\tand r5,r1,r1,lsl #1\n" + "\tasrs r5,r5,#31\n" + "\teors r1,r1,r5\n" + "\tsubs r1,r1,r5\n" + "\tldm r2!,{r6}\n" + "\tldr r7,[r2,#32]\n" + "\teors r4,r4,r6\n" + "\teors r5,r5,r7\n" + "\teors r4,r4,r5\n" + "\tstm r3!,{r5}\n" + "\tpush {r1,r2,r3}\n" + "\tldm r2,{r1-r3,r5-r9}\n" + "\teors r1,r1,r4\n" + "\teors r2,r2,r4\n" + "\teors r3,r3,r4\n" + "\teors r5,r5,r4\n" + "\teors r6,r6,r4\n" + "\teors r7,r7,r4\n" + "\teor r8,r8,r4\n" + "\teor r9,r9,r4\n" + "\tsubs r1,r1,r4\n" + "\tsbcs r2,r2,r4\n" + "\tsbcs r3,r3,r4\n" + "\tsbcs r5,r5,r4\n" + "\tsbcs r6,r6,r4\n" + "\tsbcs r7,r7,r4\n" + "\tsbcs r8,r8,r4\n" + "\tsbcs r9,r9,r4\n" + "\tsbcs r4,r4,r4\n" + "\tlsrs lr,r4,#31\n" + "\tmov r12,#0\n" + "\tldrd r10,r11,P256_order_local\n" + "\tumaal r1,r12,lr,r10\n" + "\tumaal r2,r12,lr,r11\n" + "\tldrd r10,r11,P256_order_local+8\n" + "\tumaal r3,r12,lr,r10\n" + "\tumaal r5,r12,lr,r11\n" + "\tumaal r6,r12,lr,r4\n" + "\tumaal r7,r12,lr,r4\n" + "\tmov r10,#0\n" + "\tumaal r8,r12,lr,r10\n" + "\tumaal r9,r12,lr,r4\n" + "\tumull r11,lr,r10,r10\n" + "\tumull r10,lr,r0,r1\n" + "\tumull r1,r12,r11,r11\n" + "\tumaal r11,lr,r0,r2\n" + "\tumaal r1,lr,r0,r3\n" + "\tumull r2,r3,r12,r12\n" + "\tumaal r2,lr,r0,r5\n" + "\tumaal r3,lr,r0,r6\n" + "\tumull r4,r5,r12,r12\n" + "\tumaal r4,lr,r0,r7\n" + "\tumaal r5,lr,r0,r8\n" + "\tumaal r12,lr,r0,r9\n" + "\tpop {r0,r6}\n" + "\tadds r6,r6,#36\n" + "\tldm r6!,{r8,r9}\n" + "\tmovs r7,#0\n" + "\tumaal r10,r7,r0,r8\n" + "\tumaal r11,r7,r0,r9\n" + "\tldm r6!,{r8,r9}\n" + "\tumaal r1,r7,r0,r8\n" + "\tumaal r2,r7,r0,r9\n" + "\tldm r6!,{r8,r9}\n" + "\tumaal r3,r7,r0,r8\n" + "\tumaal r4,r7,r0,r9\n" + "\tldm r6!,{r8,r9}\n" + "\tumaal r5,r7,r0,r8\n" + "\tumaal r12,r7,r0,r9\n" + "\tadd lr,lr,r7\n" + "\tldr r0,=0xee00bc4f\n" + "\tmul r0,r10,r0\n" + "\tmovs r6,#0\n" + "\tldrd r8,r9,P256_order_local\n" + "\tumaal r10,r6,r0,r8\n" + "\tumaal r11,r6,r0,r9\n" + "\tsubs r11,r11,r8\n" + "\tldrd r8,r10,P256_order_local+8\n" + "\tumaal r1,r6,r0,r8\n" + "\tsbcs r1,r1,r9\n" + "\tumaal r2,r6,r0,r10\n" + "\tmov r9,#-1\n" + "\tumaal r3,r6,r0,r9\n" + "\tumaal r4,r6,r0,r9\n" + "\tmovs r7,#0\n" + "\tumaal r5,r6,r0,r7\n" + "\tumaal r12,r6,r0,r9\n" + "\tumaal lr,r6,r7,r7\n" + "\tsbcs r2,r2,r8\n" + "\tsbcs r3,r3,r10\n" + "\tsbcs r4,r4,r9\n" + "\tsbcs r5,r5,r9\n" + "\tsbcs r12,r12,r7\n" + "\tsbcs lr,lr,r9\n" + "\tsbcs r6,r6,r7\n" + "\tldrd r0,r9,P256_order_local\n" + "\tlsrs r6,r6,#31\n" + "\tumaal r7,r11,r6,r0\n" + "\tumaal r1,r11,r6,r9\n" + "\tumaal r2,r11,r6,r8\n" + "\tumaal r3,r11,r6,r10\n" + "\trsbs r0,r6,#0\n" + "\tumaal r4,r11,r6,r0\n" + "\tumaal r5,r11,r6,r0\n" + "\tmov r8,#0\n" + "\tumaal r11,r12,r6,r8\n" + "\tumaal r12,lr,r6,r0\n" + "\tpop {r6}\n" + "\tstm r6!,{r7}\n" + "\tstm r6!,{r1,r2,r3,r4,r5,r11,r12}\n" + "\tpop {r4-r11,pc}\n" + "\t.ltorg\n" + "\t.size P256_matrix_mul_mod_n, .-P256_matrix_mul_mod_n\n" + "\t.align 2\n" + "P256_order_local:\n" + "\t.type P256_order, %object\n" + "P256_order:\n" + "\t.global P256_order\n" + "\t.word 0xFC632551\n" + "\t.word 0xF3B9CAC2\n" + "\t.word 0xA7179E84\n" + "\t.word 0xBCE6FAAD\n" + "\t.word 0xFFFFFFFF\n" + "\t.word 0xFFFFFFFF\n" + "\t.word 0\n" + "\t.word 0xFFFFFFFF\n" + "\t.word 0\n" + "\t.size P256_order, .-P256_order\n" + "\t.type P256_check_range_n, %function\n" + "P256_check_range_n:\n" + "\t.global P256_check_range_n\n" + "\tpush {r4-r11,lr}\n" + "\tldm r0,{r1-r8}\n" + "\torrs r0,r1,r2\n" + "\torrs r0,r3\n" + "\torrs r0,r4\n" + "\torrs r0,r5\n" + "\torrs r0,r6\n" + "\torrs r0,r7\n" + "\torrs r0,r8\n" + "\tbeq 0f\n" + "\tadr r0,P256_order\n" + "\tldm r0!,{r9-r12}\n" + "\tsubs r1,r9\n" + "\tsbcs r2,r2,r10\n" + "\tsbcs r3,r3,r11\n" + "\tsbcs r4,r4,r12\n" + "\tldm r0,{r0-r3}\n" + "\tsbcs r5,r5,r0\n" + "\tsbcs r6,r6,r1\n" + "\tsbcs r7,r7,r2\n" + "\tsbcs r8,r8,r3\n" + "\tsbcs r0,r0,r0\n" + "\tlsrs r0,#31\n" + "0:\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_check_range_n, .-P256_check_range_n\n" + "\t.align 2\n" + "b_mont:\n" + "\t.word 0x29c4bddf\n" + "\t.word 0xd89cdf62\n" + "\t.word 0x78843090\n" + "\t.word 0xacf005cd\n" + "\t.word 0xf7212ed6\n" + "\t.word 0xe5a220ab\n" + "\t.word 0x04874834\n" + "\t.word 0xdc30061d\n" + "three_mont:\n" + "\t.word 0x3\n" + "\t.word 0x0\n" + "\t.word 0x0\n" + "\t.word 0xfffffffd\n" + "\t.word 0xffffffff\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffc\n" + "\t.word 0x2\n" + "\t.type P256_point_is_on_curve, %function\n" + "P256_point_is_on_curve:\n" + "\t.global P256_point_is_on_curve\n" + "\tpush {r0,r4-r11,lr}\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r0,[sp,#32]\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tmov r1,sp\n" + "\tadr r2,three_mont\n" + "\tbl P256_submod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#64]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_submod\n" + "\tadr r8,b_mont\n" + "\tldm r8!,{r9-r12}\n" + "\teors r0,r9\n" + "\tittt eq\n" + "\teorseq r1,r10\n" + "\teorseq r2,r11\n" + "\teorseq r3,r12\n" + "\tldm r8,{r9-r12}\n" + "\titttt eq\n" + "\teorseq r4,r9\n" + "\teorseq r5,r10\n" + "\teorseq r6,r11\n" + "\teorseq r7,r12\n" + "\tmov r0,#0\n" + "\tit eq\n" + "\tmoveq r0,#1\n" + "\tadd sp,#68\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_point_is_on_curve, .-P256_point_is_on_curve\n" + "\t.align 2\n" + "P256_p:\n" + "\t.word 0xffffffff\n" + "\t.word 0xffffffff\n" + "\t.word 0xffffffff\n" + "\t.word 0\n" + "\t.word 0\n" + "\t.word 0\n" + "\t.word 1\n" + "\t.word 0xffffffff\n" + "\t.type P256_jacobian_to_affine, %function\n" + "P256_jacobian_to_affine:\n" + "\t.global P256_jacobian_to_affine\n" + "\tpush {r0,r1,r2,r4-r11,lr}\n" + "\tadds r2,#64\n" + "\tldm r2,{r0-r7}\n" + "\tmov r8,#0\n" + "\tbl P256_modinv_sqrt\n" + "\tpush {r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmov r1,sp\n" + "\tldr r2,[sp,#72]\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#64]\n" + "\tstm r8,{r0-r7}\n" + "\tldr r2,[sp,#72]\n" + "\tadd r1,sp,#32\n" + "\tadds r2,r2,#32\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#68]\n" + "\tstm r8,{r0-r7}\n" + "\tadd sp,#76\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_jacobian_to_affine, .-P256_jacobian_to_affine\n" + "\t.type P256_double_j, %function\n" + "P256_double_j:\n" + "\t.global P256_double_j\n" + "\tpush {r0,r1,r4-r11,lr}\n" + "\tadds r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#36]\n" + "\tadds r1,#32\n" + "\tadd r2,r1,#32\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#32]\n" + "\tadd r8,#64\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#36]\n" + "\tmov r2,sp\n" + "\tbl P256_addmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#68]\n" + "\tadd r2,sp,#32\n" + "\tbl P256_submod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tlsl r8,r0,#31\n" + "\tadds r0,r0,r8, asr #31\n" + "\tadcs r1,r1,r8, asr #31\n" + "\tadcs r2,r2,r8, asr #31\n" + "\tadcs r3,r3,#0\n" + "\tadcs r4,r4,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r6,r8, lsr #31\n" + "\tadcs r7,r7,r8, asr #31\n" + "\trrxs r7,r7\n" + "\trrxs r6,r6\n" + "\trrxs r5,r5\n" + "\trrxs r4,r4\n" + "\trrxs r3,r3\n" + "\trrxs r2,r2\n" + "\trrxs r1,r1\n" + "\trrx r0,r0\n" + "\tstm sp,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_addmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r0,[sp,#68]\n" + "\tadds r0,#32\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tldr r8,[sp,#64]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldrd r0,r1,[sp,#96]\n" + "\tadd r2,r0,#32\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_times2\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,r8\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tmov r2,r8\n" + "\tadd r1,r2,#32\n" + "\tbl P256_submod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#64\n" + "\tadd r2,sp,#32\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#64\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#64\n" + "\tmov r2,sp\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd sp,#104\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_double_j, .-P256_double_j\n" + "\t.type add_sub_helper, %function\n" + "add_sub_helper:\n" + "\tpush {lr}\n" + "\tldm r1!,{r5-r12}\n" + "\tstm r0!,{r5-r12}\n" + "\tldm r1!,{r5-r12}\n" + "\tcbz r2,0f\n" + "\tmov lr,#0\n" + "\trsbs r4,r2,#0\n" + "\tsubs r5,r4,r5\n" + "\tsbcs r6,r4,r6\n" + "\tsbcs r7,r4,r7\n" + "\tsbcs r8,lr,r8\n" + "\tsbcs r9,lr,r9\n" + "\tsbcs r10,lr,r10\n" + "\tsbcs r11,r2,r11\n" + "\tsbcs r12,r4,r12\n" + "0:\n" + "\tstm r0!,{r5-r12}\n" + "\tcbnz r3,1f\n" + "\tldm r1,{r5-r12}\n" + "\tstm r0,{r5-r12}\n" + "\tb 2f\n" + "1:\n" + "\tmovs r4,#0\n" + "\tumull r5,r10,r4,r4\n" + "\tmvns r6,r4\n" + "\tmvns r7,r4\n" + "\tmov r8,#0xffffffff\n" + "\tmov r9,#0xfffffffe\n" + "\tstm r0,{r3-r10}\n" + "2:\n" + "\tpop {pc}\n" + "\t.size add_sub_helper, .-add_sub_helper\n" + "\t.type P256_add_sub_j, %function\n" + "P256_add_sub_j:\n" + "\t.global P256_add_sub_j\n" + "\tpush {r0-r11,lr}\n" + "\tadd r4,r0,#64\n" + "\tldm r4,{r4-r11}\n" + "\torrs r4,r5\n" + "\torrs r4,r6\n" + "\torrs r4,r7\n" + "\torrs r4,r8\n" + "\torrs r4,r9\n" + "\torrs r4,r10\n" + "\torrs r4,r11\n" + "\tbne 2f\n" + "\tbl add_sub_helper\n" + "\tadd sp,#16\n" + "\tpop {r4-r11,pc}\n" + "2:\n" + "\tcbnz r3,100f\n" + "\tadds r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#32]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#32]\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#36]\n" + "\tadds r1,#64\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#32]\n" + "\tadds r1,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#32]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tb 101f\n" + "100:\n" + "\tsub sp,#32\n" + "101:\n" + "\tldr r1,[sp,#32]\n" + "\tadds r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#68]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#64]\n" + "\tadds r1,#64\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#68]\n" + "\tadds r1,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#64]\n" + "\tadd r2,sp,#32\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#64]\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r2,[sp,#96]\n" + "\tadd r1,r2,#64\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#64\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#108]\n" + "\tcbnz r1,102f\n" + "\tldr r1,[sp,#100]\n" + "\tadds r1,#64\n" + "\tmov r2,r8\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#64\n" + "\tstm r8,{r0-r7}\n" + "102:\n" + "\tldr r1,[sp,#96]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\torrs r1,r0\n" + "\torrs r1,r2\n" + "\torrs r1,r3\n" + "\torrs r1,r4\n" + "\torrs r1,r5\n" + "\torrs r1,r6\n" + "\torrs r0,r1,r7\n" + "3:\n" + "\tpush {r0}\n" + "\tldr r1,[sp,#100]\n" + "\tadds r1,#32\n" + "\tadd r2,sp,#36\n" + "\tldr r3,[sp,#108]\n" + "\tcbz r3,4f\n" + "\tbl P256_addmod\n" + "\tb 5f\n" + "4:\n" + "\tbl P256_submod\n" + "5:\n" + "\tldr r8,[sp,#100]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r8}\n" + "\torrs r1,r0\n" + "\torrs r1,r2\n" + "\torrs r1,r3\n" + "\torrs r1,r4\n" + "\torrs r1,r5\n" + "\torrs r1,r6\n" + "\torrs r1,r7\n" + "\torrs r1,r8\n" + "\tbne 6f\n" + "\tadd sp,#96\n" + "\tldm sp,{r0-r3}\n" + "\tbl add_sub_helper\n" + "\tldr r0,[sp,#0]\n" + "\tmov r1,r0\n" + "\tadd sp,#16\n" + "\tbl P256_double_j\n" + "\tpop {r4-r11,pc}\n" + "6:\n" + "\tadd r1,sp,#64\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#64\n" + "\tstm r8,{r0-r7}\n" + "\tldr r0,[sp,#96]\n" + "\tadds r0,#32\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tstm sp,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tldr r2,[sp,#96]\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmov r1,sp\n" + "\tldr r2,[sp,#96]\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tadd r0,sp,#64\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_times2\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#96]\n" + "\tmov r2,sp\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#64\n" + "\tldr r2,[sp,#96]\n" + "\tbl P256_submod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#96]\n" + "\tadds r1,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r0,[sp,#104]\n" + "\tmov r1,sp\n" + "\tadd r2,sp,#32\n" + "\tcbz r0,7f\n" + "\tbl P256_addmod\n" + "\tb 8f\n" + "7:\n" + "\tbl P256_submod\n" + "8:\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd sp,#112\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_add_sub_j, .-P256_add_sub_j\n" + "\t.type P256_verify_last_step, %function\n" + "P256_verify_last_step:\n" + "\t.global P256_verify_last_step\n" + "\tpush {r0,r1,r4-r11,lr}\n" + "\tsub sp,#32\n" + "\tadd r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\torrs r0,r1\n" + "\torrs r0,r2\n" + "\torrs r0,r3\n" + "\torrs r0,r4\n" + "\torrs r0,r5\n" + "\torrs r0,r6\n" + "\torrs r0,r7\n" + "\tbeq 0f\n" + "\tldr r1,[sp,#64]\n" + "2:\n" + "\tadd r0,sp,#32\n" + "\tbl P256_to_montgomery\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#68]\n" + "\tldm r8!,{r9-r12}\n" + "\teors r0,r9\n" + "\tittt eq\n" + "\teorseq r1,r10\n" + "\teorseq r2,r11\n" + "\teorseq r3,r12\n" + "\tldm r8!,{r9-r12}\n" + "\titttt eq\n" + "\teorseq r4,r9\n" + "\teorseq r5,r10\n" + "\teorseq r6,r11\n" + "\teorseq r7,r12\n" + "\tmov r0,#1\n" + "\tbeq 1f\n" + "\tadr r0,P256_order\n" + "\tldm r0,{r8-r11}\n" + "\tldr r0,[sp,#64]\n" + "\tcbz r0,0f\n" + "\tldm r0,{r0-r7}\n" + "\tadds r0,r8\n" + "\tadcs r1,r1,r9\n" + "\tadcs r2,r2,r10\n" + "\tadcs r3,r3,r11\n" + "\tadcs r4,r4,#0xffffffff\n" + "\tadcs r5,r5,#0xffffffff\n" + "\tadcs r6,r6,#0\n" + "\tadcs r7,r7,#0xffffffff\n" + "\tbcs 0f\n" + "\tsubs r8,r0,#0xffffffff\n" + "\tsbcs r8,r1,#0xffffffff\n" + "\tsbcs r8,r2,#0xffffffff\n" + "\tsbcs r8,r3,#0\n" + "\tsbcs r8,r4,#0\n" + "\tsbcs r8,r5,#0\n" + "\tsbcs r8,r6,#1\n" + "\tsbcs r8,r7,#0xffffffff\n" + "\tbcs 0f\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmovs r2,#0\n" + "\tstr r2,[sp,#64]\n" + "\tmov r1,r8\n" + "\tb 2b\n" + "0:\n" + "\tmovs r0,#0\n" + "1:\n" + "\tadd sp,#72\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_verify_last_step, .-P256_verify_last_step\n" + "\t.type P256_negate_mod_m_if, %function\n" + "P256_negate_mod_m_if:\n" + "\tpush {r4-r8,lr}\n" + "\trsb r8,r2,#1\n" + "\tmovs r6,#8\n" + "\tsubs r7,r7\n" + "0:\n" + "\tldm r1!,{r4,r12}\n" + "\tldm r3!,{r5,lr}\n" + "\tsbcs r5,r5,r4\n" + "\tumull r4,r7,r8,r4\n" + "\tumaal r4,r7,r2,r5\n" + "\tsbcs lr,lr,r12\n" + "\tumull r12,r7,r8,r12\n" + "\tumaal r12,r7,r2,lr\n" + "\tstm r0!,{r4,r12}\n" + "\tsub r6,#2\n" + "\tcbz r6,1f\n" + "\tb 0b\n" + "1:\n" + "\tpop {r4-r8,pc}\n" + "\t.size P256_negate_mod_m_if, .-P256_negate_mod_m_if\n" + "\t.type P256_negate_mod_n_if, %function\n" + "P256_negate_mod_n_if:\n" + "\t.global P256_negate_mod_n_if\n" + "\tldr r3,=P256_order\n" + "\tb P256_negate_mod_m_if\n" + "\t.size P256_negate_mod_n_if, .-P256_negate_mod_n_if\n" + "\t.type P256_negate_mod_p_if, %function\n" + "P256_negate_mod_p_if:\n" + "\t.global P256_negate_mod_p_if\n" + "\tadr r3,P256_p\n" + "\tb P256_negate_mod_m_if\n" + "\t.size P256_negate_mod_p_if, .-P256_negate_mod_p_if\n" + "\t.align 2\n" + ".popsection\n"); // No .end here: it would drop the C code that follows + +// mg_p256_*() wrappers, see tls_p256.h. The API uses big endian byte strings, +// this library uses little endian 32-bit words, so convert both ways + +int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]) { + uint32_t x[8], y[8], d[8]; + int tries, ok = 0; + for (tries = 0; tries < 64 && !ok; tries++) { + if (!mg_random(d, sizeof(d))) break; + ok = p256_keygen(x, y, d); + } + if (ok) { + p256_convert_endianness(public_key, x, 32); + p256_convert_endianness(public_key + 32, y, 32); + p256_convert_endianness(private_key, d, 32); + } + mg_bzero((unsigned char *) d, sizeof(d)); + return ok; +} + +int mg_p256_compute_public_key(const uint8_t private_key[32], + uint8_t public_key[64]) { + uint32_t x[8], y[8], d[8]; + int ok; + p256_convert_endianness(d, private_key, 32); + ok = p256_keygen(x, y, d); // False if the key is out of range + if (ok) { + p256_convert_endianness(public_key, x, 32); + p256_convert_endianness(public_key + 32, y, 32); + } + mg_bzero((unsigned char *) d, sizeof(d)); + return ok; +} + +int mg_p256_shared_secret(const uint8_t public_key[64], + const uint8_t private_key[32], uint8_t secret[32]) { + uint32_t x[8], y[8], d[8]; + int ok; + p256_convert_endianness(x, public_key, 32); + p256_convert_endianness(y, public_key + 32, 32); + p256_convert_endianness(d, private_key, 32); + ok = p256_ecdh_calc_shared_secret(secret, d, x, y); // Validates the point + mg_bzero((unsigned char *) d, sizeof(d)); + return ok; +} + +int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, const uint8_t k[32], + uint8_t signature[64]) { + uint32_t r[8], s[8], d[8], kw[8]; + int ok; + p256_convert_endianness(d, private_key, 32); + p256_convert_endianness(kw, k, 32); + ok = p256_sign(r, s, hash, (uint32_t) hash_size, d, kw); // False if k is + // out of range + if (ok) { + p256_convert_endianness(signature, r, 32); + p256_convert_endianness(signature + 32, s, 32); + } + mg_bzero((unsigned char *) d, sizeof(d)); + mg_bzero((unsigned char *) kw, sizeof(kw)); + return ok; +} + +int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash, + size_t hash_size, const uint8_t signature[64]) { + uint32_t x[8], y[8], r[8], s[8]; + p256_convert_endianness(x, public_key, 32); + p256_convert_endianness(y, public_key + 32, 32); + p256_convert_endianness(r, signature, 32); + p256_convert_endianness(s, signature + 32, 32); + return p256_verify(x, y, hash, (uint32_t) hash_size, r, s); // Checks point +} +#undef get_bit +#endif // MG_P256 == MG_P256_LENNGREN + +#ifdef MG_ENABLE_LINES +#line 1 "src/tls_p384.c" +#endif +// P-384 from BearSSL, https://bearssl.org commit 7bea48e +// P-384 signature verification, used by built-in TLS for certificates and +// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c +// +// Copyright (c) 2016 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static +// functions with p384_. Trimmed to P-384. Structs are not typedef-ed + + + + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + +// From BearSSL src/ec/ec_secp384r1.c +static const unsigned char P384_N[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, + 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, + 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 +}; + +static const unsigned char P384_G[] = { + 0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, + 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, + 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, + 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, + 0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, + 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, + 0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, + 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, + 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, + 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, + 0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, + 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, + 0x5F +}; + + +// From BearSSL src/ec/ec_prime_i31.c +/* + * Parameters for supported curves (field modulus, and 'b' equation + * parameter; both values use the 'i31' format, and 'b' is in Montgomery + * representation). + */ + +static const uint32_t P384_P[] = { + 0x0000018C, + 0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8, + 0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x00000FFF +}; + +static const uint32_t P384_R2[] = { + 0x0000018C, + 0x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF, + 0x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF, + 0x00008000, 0x00008000, 0x00000000, 0x00000000, + 0x00000000 +}; + +static const uint32_t P384_B[] = { + 0x0000018C, + 0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C, + 0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B, + 0x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE, + 0x000008A5 +}; + +struct p384_curve_params { + const uint32_t *p; + const uint32_t *b; + const uint32_t *R2; + uint32_t p0i; + size_t point_len; +}; + +static inline const struct p384_curve_params * +p384_id_to_curve(int curve) +{ + static const struct p384_curve_params pp = { + P384_P, P384_B, P384_R2, 0x00000001, 97 + }; + + (void)curve; + return &pp; +} + +#define I31_LEN ((MG_BR_MAX_EC_SIZE + 61) / 31) + +/* + * Type for a point in Jacobian coordinates: + * -- three values, x, y and z, in Montgomery representation + * -- affine coordinates are X = x / z^2 and Y = y / z^3 + * -- for the point at infinity, z = 0 + */ +struct p384_jacobian { + uint32_t c[3][I31_LEN]; +}; + +/* + * We use a custom interpreter that uses a dozen registers, and + * only six operations: + * MSET(d, a) copy a into d + * MADD(d, a) d = d+a (modular) + * MSUB(d, a) d = d-a (modular) + * MMUL(d, a, b) d = a*b (Montgomery multiplication) + * MINV(d, a, b) invert d modulo p; a and b are used as scratch registers + * MTZ(d) clear return value if d = 0 + * Destination of MMUL (d) must be distinct from operands (a and b). + * There is no such constraint for MSUB and MADD. + * + * Registers include the operand coordinates, and temporaries. + */ +#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4)) +#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4)) +#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4)) +#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b)) +#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b)) +#define MTZ(d) (0x5000 + ((d) << 8)) +#define ENDCODE 0 + +/* + * Registers for the input operands. + */ +#define P1x 0 +#define P1y 1 +#define P1z 2 +#define P2x 3 +#define P2y 4 +#define P2z 5 + +/* + * Alternate names for the first input operand. + */ +#define Px 0 +#define Py 1 +#define Pz 2 + +/* + * Temporaries. + */ +#define t1 6 +#define t2 7 +#define t3 8 +#define t4 9 +#define t5 10 +#define t6 11 +#define t7 12 + +/* + * Extra scratch registers available when there is no second operand (e.g. + * for "double" and "affine"). + */ +#define t8 3 +#define t9 4 +#define t10 5 + +/* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * If y = 0 (P has order 2) then this yields infinity (z' = 0), as it + * should. This case should not happen anyway, because our curves have + * prime order, and thus do not contain any point of order 2. + * + * If P is infinity (z = 0), then again the formulas yield infinity, + * which is correct. Thus, this code works for all points. + * + * Cost: 8 multiplications + */ +static const uint16_t p384_code_double[] = { + /* + * Compute z^2 (in t1). + */ + MMUL(t1, Pz, Pz), + + /* + * Compute x-z^2 (in t2) and then x+z^2 (in t1). + */ + MSET(t2, Px), + MSUB(t2, t1), + MADD(t1, Px), + + /* + * Compute m = 3*(x+z^2)*(x-z^2) (in t1). + */ + MMUL(t3, t1, t2), + MSET(t1, t3), + MADD(t1, t3), + MADD(t1, t3), + + /* + * Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + MMUL(t3, Py, Py), + MADD(t3, t3), + MMUL(t2, Px, t3), + MADD(t2, t2), + + /* + * Compute x' = m^2 - 2*s. + */ + MMUL(Px, t1, t1), + MSUB(Px, t2), + MSUB(Px, t2), + + /* + * Compute z' = 2*y*z. + */ + MMUL(t4, Py, Pz), + MSET(Pz, t4), + MADD(Pz, t4), + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + MSUB(t2, Px), + MMUL(Py, t1, t2), + MMUL(t4, t3, t3), + MSUB(Py, t4), + MSUB(Py, t4), + + ENDCODE +}; + +/* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + * + * If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that + * z3 == 0, so the result is correct. + * If either of P1 or P2 is infinity, but not both, then z3 == 0, which is + * not correct. + * h == 0 only if u1 == u2; this happens in two cases: + * -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2 + * -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity) + * + * Thus, the following situations are not handled correctly: + * -- P1 = 0 and P2 != 0 + * -- P1 != 0 and P2 = 0 + * -- P1 = P2 + * All other cases are properly computed. However, even in "incorrect" + * situations, the three coordinates still are properly formed field + * elements. + * + * The returned flag is cleared if r == 0. This happens in the following + * cases: + * -- Both points are on the same horizontal line (same Y coordinate). + * -- Both points are infinity. + * -- One point is infinity and the other is on line Y = 0. + * The third case cannot happen with our curves (there is no valid point + * on line Y = 0 since that would be a point of order 2). If the two + * source points are non-infinity, then remains only the case where the + * two points are on the same horizontal line. + * + * This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and + * P2 != 0: + * -- If the returned value is not the point at infinity, then it was properly + * computed. + * -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result + * is indeed the point at infinity. + * -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should + * use the 'double' code. + * + * Cost: 16 multiplications + */ +static const uint16_t p384_code_add[] = { + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + MMUL(t3, P2z, P2z), + MMUL(t1, P1x, t3), + MMUL(t4, P2z, t3), + MMUL(t3, P1y, t4), + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + MMUL(t4, P1z, P1z), + MMUL(t2, P2x, t4), + MMUL(t5, P1z, t4), + MMUL(t4, P2y, t5), + + /* + * Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4). + */ + MSUB(t2, t1), + MSUB(t4, t3), + + /* + * Report cases where r = 0 through the returned flag. + */ + MTZ(t4), + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5). + */ + MMUL(t7, t2, t2), + MMUL(t6, t1, t7), + MMUL(t5, t7, t2), + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + * t1 and t7 can be used as scratch registers. + */ + MMUL(P1x, t4, t4), + MSUB(P1x, t5), + MSUB(P1x, t6), + MSUB(P1x, t6), + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + MSUB(t6, P1x), + MMUL(P1y, t4, t6), + MMUL(t1, t5, t3), + MSUB(P1y, t1), + + /* + * Compute z3 = h*z1*z2. + */ + MMUL(t1, P1z, P2z), + MMUL(P1z, t1, t2), + + ENDCODE +}; + +/* + * Check that the point is on the curve. This code snippet assumes the + * following conventions: + * -- Coordinates x and y have been freshly decoded in P1 (but not + * converted to Montgomery coordinates yet). + * -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1. + */ +static const uint16_t p384_code_check[] = { + + /* Convert x and y to Montgomery representation. */ + MMUL(t1, P1x, P2x), + MMUL(t2, P1y, P2x), + MSET(P1x, t1), + MSET(P1y, t2), + + /* Compute x^3 in t1. */ + MMUL(t2, P1x, P1x), + MMUL(t1, P1x, t2), + + /* Subtract 3*x from t1. */ + MSUB(t1, P1x), + MSUB(t1, P1x), + MSUB(t1, P1x), + + /* Add b. */ + MADD(t1, P2y), + + /* Compute y^2 in t2. */ + MMUL(t2, P1y, P1y), + + /* Compare y^2 with x^3 - 3*x + b; they must match. */ + MSUB(t1, t2), + MTZ(t1), + + /* Set z to 1 (in Montgomery representation). */ + MMUL(P1z, P2x, P2z), + + ENDCODE +}; + +/* + * Conversion back to affine coordinates. This code snippet assumes that + * the z coordinate of P2 is set to 1 (not in Montgomery representation). + */ +static const uint16_t p384_code_affine[] = { + + /* Save z*R in t1. */ + MSET(t1, P1z), + + /* Compute z^3 in t2. */ + MMUL(t2, P1z, P1z), + MMUL(t3, P1z, t2), + MMUL(t2, t3, P2z), + + /* Invert to (1/z^3) in t2. */ + MINV(t2, t3, t4), + + /* Compute y. */ + MSET(t3, P1y), + MMUL(P1y, t2, t3), + + /* Compute (1/z^2) in t3. */ + MMUL(t3, t2, t1), + + /* Compute x. */ + MSET(t2, P1x), + MMUL(P1x, t2, t3), + + ENDCODE +}; + +static uint32_t +p384_run_code(struct p384_jacobian *P1, const struct p384_jacobian *P2, + const struct p384_curve_params *cc, const uint16_t *code) +{ + uint32_t r; + uint32_t t[13][I31_LEN]; + size_t u; + + r = 1; + + /* + * Copy the two operands in the dedicated registers. + */ + memcpy(t[P1x], P1->c, 3 * I31_LEN * sizeof(uint32_t)); + memcpy(t[P2x], P2->c, 3 * I31_LEN * sizeof(uint32_t)); + + /* + * Run formulas. + */ + for (u = 0;; u ++) { + unsigned op, d, a, b; + + op = code[u]; + if (op == 0) { + break; + } + d = (op >> 8) & 0x0F; + a = (op >> 4) & 0x0F; + b = op & 0x0F; + op >>= 12; + switch (op) { + uint32_t ctl; + size_t plen; + unsigned char tp[(MG_BR_MAX_EC_SIZE + 7) >> 3]; + + case 0: + memcpy(t[d], t[a], I31_LEN * sizeof(uint32_t)); + break; + case 1: + ctl = mg_br_i31_add(t[d], t[a], 1); + ctl |= MG_BR_NOT(mg_br_i31_sub(t[d], cc->p, 0)); + mg_br_i31_sub(t[d], cc->p, ctl); + break; + case 2: + mg_br_i31_add(t[d], cc->p, mg_br_i31_sub(t[d], t[a], 1)); + break; + case 3: + mg_br_i31_montymul(t[d], t[a], t[b], cc->p, cc->p0i); + break; + case 4: + plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3; + mg_br_i31_encode(tp, plen, cc->p); + tp[plen - 1] -= 2; + mg_br_i31_modpow(t[d], tp, plen, + cc->p, cc->p0i, t[a], t[b]); + break; + default: + r &= ~mg_br_i31_iszero(t[d]); + break; + } + } + + /* + * Copy back result. + */ + memcpy(P1->c, t[P1x], 3 * I31_LEN * sizeof(uint32_t)); + return r; +} + +static void +p384_set_one(uint32_t *x, const uint32_t *p) +{ + size_t plen; + + plen = (p[0] + 63) >> 5; + memset(x, 0, plen * sizeof *x); + x[0] = p[0]; + x[1] = 0x00000001; +} + +static void +p384_point_zero(struct p384_jacobian *P, const struct p384_curve_params *cc) +{ + memset(P, 0, sizeof *P); + P->c[0][0] = P->c[1][0] = P->c[2][0] = cc->p[0]; +} + +static inline void +p384_point_double(struct p384_jacobian *P, const struct p384_curve_params *cc) +{ + p384_run_code(P, P, cc, p384_code_double); +} + +static inline uint32_t +p384_point_add(struct p384_jacobian *P1, const struct p384_jacobian *P2, const struct p384_curve_params *cc) +{ + return p384_run_code(P1, P2, cc, p384_code_add); +} + +static void +p384_point_mul(struct p384_jacobian *P, const unsigned char *x, size_t xlen, + const struct p384_curve_params *cc) +{ + /* + * We do a simple double-and-add ladder with a 2-bit window + * to make only one add every two doublings. We thus first + * precompute 2P and 3P in some local buffers. + * + * We always perform two doublings and one addition; the + * addition is with P, 2P and 3P and is done in a temporary + * array. + * + * The addition code cannot handle cases where one of the + * operands is infinity, which is the case at the start of the + * ladder. We therefore need to maintain a flag that controls + * this situation. + */ + uint32_t qz; + struct p384_jacobian P2, P3, Q, T, U; + + memcpy(&P2, P, sizeof P2); + p384_point_double(&P2, cc); + memcpy(&P3, P, sizeof P3); + p384_point_add(&P3, &P2, cc); + + p384_point_zero(&Q, cc); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + p384_point_double(&Q, cc); + p384_point_double(&Q, cc); + memcpy(&T, P, sizeof T); + memcpy(&U, &Q, sizeof U); + bits = (*x >> k) & (uint32_t)3; + bnz = MG_BR_NEQ(bits, 0); + MG_BR_CCOPY(MG_BR_EQ(bits, 2), &T, &P2, sizeof T); + MG_BR_CCOPY(MG_BR_EQ(bits, 3), &T, &P3, sizeof T); + p384_point_add(&U, &T, cc); + MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q); + MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + memcpy(P, &Q, sizeof Q); +} + +/* + * Decode point into Jacobian coordinates. This function does not support + * the point at infinity. If the point is invalid then this returns 0, but + * the coordinates are still set to properly formed field elements. + */ +static uint32_t +p384_point_decode(struct p384_jacobian *P, const void *src, size_t len, const struct p384_curve_params *cc) +{ + /* + * Points must use uncompressed format: + * -- first byte is 0x04; + * -- coordinates X and Y use unsigned big-endian, with the same + * length as the field modulus. + * + * We don't support hybrid format (uncompressed, but first byte + * has value 0x06 or 0x07, depending on the least significant bit + * of Y) because it is rather useless, and explicitly forbidden + * by PKIX (RFC 5480, section 2.2). + * + * We don't support compressed format either, because it is not + * much used in practice (there are or were patent-related + * concerns about point compression, which explains the lack of + * generalised support). Also, point compression support would + * need a bit more code. + */ + const unsigned char *buf; + size_t plen, zlen; + uint32_t r; + struct p384_jacobian Q; + + buf = (const unsigned char *)src; + p384_point_zero(P, cc); + plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3; + if (len != 1 + (plen << 1)) { + return 0; + } + r = mg_br_i31_decode_mod(P->c[0], buf + 1, plen, cc->p); + r &= mg_br_i31_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p); + + /* + * Check first byte. + */ + r &= MG_BR_EQ(buf[0], 0x04); + /* obsolete + r &= MG_BR_EQ(buf[0], 0x04) | (MG_BR_EQ(buf[0] & 0xFE, 0x06) + & ~(uint32_t)(buf[0] ^ buf[plen << 1])); + */ + + /* + * Convert coordinates and check that the point is valid. + */ + zlen = ((cc->p[0] + 63) >> 5) * sizeof(uint32_t); + memcpy(Q.c[0], cc->R2, zlen); + memcpy(Q.c[1], cc->b, zlen); + p384_set_one(Q.c[2], cc->p); + r &= ~p384_run_code(P, &Q, cc, p384_code_check); + return r; +} + +/* + * Encode a point. This method assumes that the point is correct and is + * not the point at infinity. Encoded size is always 1+2*plen, where + * plen is the field modulus length, in bytes. + */ +static void +p384_point_encode(void *dst, const struct p384_jacobian *P, const struct p384_curve_params *cc) +{ + unsigned char *buf; + uint32_t xbl; + size_t plen; + struct p384_jacobian Q, T; + + buf = (unsigned char *)dst; + xbl = cc->p[0]; + xbl -= (xbl >> 5); + plen = (xbl + 7) >> 3; + buf[0] = 0x04; + memcpy(&Q, P, sizeof *P); + p384_set_one(T.c[2], cc->p); + p384_run_code(&Q, &T, cc, p384_code_affine); + mg_br_i31_encode(buf + 1, plen, Q.c[0]); + mg_br_i31_encode(buf + 1 + plen, plen, Q.c[1]); +} + +static const unsigned char * +p384_api_generator(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P384_G; + return P384_G; +} + +static const unsigned char * +p384_api_order(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P384_N; + return P384_N; +} + +static size_t +p384_api_xoff(int curve, size_t *len) +{ + p384_api_generator(curve, len); + *len >>= 1; + return 1; +} + +static uint32_t +p384_api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + const struct p384_curve_params *cc; + struct p384_jacobian P; + + cc = p384_id_to_curve(curve); + if (Glen != cc->point_len) { + return 0; + } + r = p384_point_decode(&P, G, Glen, cc); + p384_point_mul(&P, x, xlen, cc); + p384_point_encode(G, &P, cc); + return r; +} + +static size_t +p384_api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + const unsigned char *G; + size_t Glen; + + G = p384_api_generator(curve, &Glen); + memcpy(R, G, Glen); + p384_api_mul(R, Glen, x, xlen, curve); + return Glen; +} + +static uint32_t +p384_api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + uint32_t r, t, z; + const struct p384_curve_params *cc; + struct p384_jacobian P, Q; + + /* + * TODO: see about merging the two ladders. Right now, we do + * two independent point multiplications, which is a bit + * wasteful of CPU resources (but yields short code). + */ + + cc = p384_id_to_curve(curve); + if (len != cc->point_len) { + return 0; + } + r = p384_point_decode(&P, A, len, cc); + if (B == NULL) { + size_t Glen; + + B = p384_api_generator(curve, &Glen); + } + r &= p384_point_decode(&Q, B, len, cc); + p384_point_mul(&P, x, xlen, cc); + p384_point_mul(&Q, y, ylen, cc); + + /* + * We want to compute P+Q. Since the base points A and B are distinct + * from infinity, and the multipliers are non-zero and lower than the + * curve order, then we know that P and Q are non-infinity. This + * leaves two special situations to test for: + * -- If P = Q then we must use p384_point_double(). + * -- If P+Q = 0 then we must report an error. + */ + t = p384_point_add(&P, &Q, cc); + p384_point_double(&Q, cc); + z = mg_br_i31_iszero(P.c[2]); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + MG_BR_CCOPY(z & ~t, &P, &Q, sizeof Q); + p384_point_encode(A, &P, cc); + r &= ~(z & t); + + return r; +} + +static const struct mg_br_ec_impl mg_br_ec_prime_i31 = { + (uint32_t)1 << MG_BR_EC_secp384r1, + &p384_api_generator, + &p384_api_order, + &p384_api_xoff, + &p384_api_mul, + &p384_api_mulgen, + &p384_api_muladd +}; + +// See tls_p384.h +int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash, + size_t hash_size, const uint8_t signature[96]) { + unsigned char q[97]; + q[0] = 0x04; + memcpy(q + 1, public_key, 96); + return mg_br_ecdsa_i31_vrfy_raw(&mg_br_ec_prime_i31, MG_BR_EC_secp384r1, hash, + hash_size, q, sizeof(q), signature, 96) == 1; +} + +// Undefine local macros, mongoose.c is one translation unit +#undef I31_LEN +#undef MSET +#undef MADD +#undef MSUB +#undef MMUL +#undef MINV +#undef MTZ +#undef ENDCODE +#undef P1x +#undef P1y +#undef P1z +#undef P2x +#undef P2y +#undef P2z +#undef Px +#undef Py +#undef Pz +#undef t1 +#undef t2 +#undef t3 +#undef t4 +#undef t5 +#undef t6 +#undef t7 +#undef t8 +#undef t9 +#undef t10 +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + #ifdef MG_ENABLE_LINES #line 1 "src/tls_rsa.c" #endif @@ -23055,3580 +28714,6 @@ bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash, #endif /* MG_TLS == MG_TLS_BUILTIN */ -#ifdef MG_ENABLE_LINES -#line 1 "src/tls_uecc.c" -#endif -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - - - - -#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) - -#ifndef MG_UECC_RNG_MAX_TRIES -#define MG_UECC_RNG_MAX_TRIES 64 -#endif - -#if MG_UECC_ENABLE_VLI_API -#define MG_UECC_VLI_API -#else -#define MG_UECC_VLI_API static -#endif - -#if (MG_UECC_PLATFORM == mg_uecc_avr) || (MG_UECC_PLATFORM == mg_uecc_arm) || \ - (MG_UECC_PLATFORM == mg_uecc_arm_thumb) || \ - (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) -#define MG_UECC_CONCATX(a, ...) a##__VA_ARGS__ -#define MG_UECC_CONCAT(a, ...) MG_UECC_CONCATX(a, __VA_ARGS__) - -#define STRX(a) #a -#define STR(a) STRX(a) - -#define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) -#define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) -#define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__)))) -#define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__)))) -#define EVAL4(...) __VA_ARGS__ - -#define DEC_1 0 -#define DEC_2 1 -#define DEC_3 2 -#define DEC_4 3 -#define DEC_5 4 -#define DEC_6 5 -#define DEC_7 6 -#define DEC_8 7 -#define DEC_9 8 -#define DEC_10 9 -#define DEC_11 10 -#define DEC_12 11 -#define DEC_13 12 -#define DEC_14 13 -#define DEC_15 14 -#define DEC_16 15 -#define DEC_17 16 -#define DEC_18 17 -#define DEC_19 18 -#define DEC_20 19 -#define DEC_21 20 -#define DEC_22 21 -#define DEC_23 22 -#define DEC_24 23 -#define DEC_25 24 -#define DEC_26 25 -#define DEC_27 26 -#define DEC_28 27 -#define DEC_29 28 -#define DEC_30 29 -#define DEC_31 30 -#define DEC_32 31 - -#define DEC_(N) MG_UECC_CONCAT(DEC_, N) - -#define SECOND_ARG(_, val, ...) val -#define SOME_CHECK_0 ~, 0 -#define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME, ) -#define SOME_OR_0(N) GET_SECOND_ARG(MG_UECC_CONCAT(SOME_CHECK_, N)) - -#define MG_UECC_EMPTY(...) -#define DEFER(...) __VA_ARGS__ MG_UECC_EMPTY() - -#define REPEAT_NAME_0() REPEAT_0 -#define REPEAT_NAME_SOME() REPEAT_SOME -#define REPEAT_0(...) -#define REPEAT_SOME(N, stuff) \ - DEFER(MG_UECC_CONCAT(REPEAT_NAME_, SOME_OR_0(DEC_(N))))()(DEC_(N), stuff) stuff -#define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff)) - -#define REPEATM_NAME_0() REPEATM_0 -#define REPEATM_NAME_SOME() REPEATM_SOME -#define REPEATM_0(...) -#define REPEATM_SOME(N, macro) \ - macro(N) DEFER(MG_UECC_CONCAT(REPEATM_NAME_, SOME_OR_0(DEC_(N))))()(DEC_(N), macro) -#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) -#endif - -// - -#if (MG_UECC_WORD_SIZE == 1) -#if MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_MAX_WORDS 21 /* Due to the size of curve_n. */ -#endif -#if MG_UECC_SUPPORTS_secp192r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 24 -#endif -#if MG_UECC_SUPPORTS_secp224r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 28 -#endif -#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1) -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 32 -#endif -#if MG_UECC_SUPPORTS_secp384r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 48 -#endif -#elif (MG_UECC_WORD_SIZE == 4) -#if MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_MAX_WORDS 6 /* Due to the size of curve_n. */ -#endif -#if MG_UECC_SUPPORTS_secp192r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 6 -#endif -#if MG_UECC_SUPPORTS_secp224r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 7 -#endif -#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1) -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 8 -#endif -#if MG_UECC_SUPPORTS_secp384r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 12 -#endif -#elif (MG_UECC_WORD_SIZE == 8) -#if MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_MAX_WORDS 3 -#endif -#if MG_UECC_SUPPORTS_secp192r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 3 -#endif -#if MG_UECC_SUPPORTS_secp224r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 4 -#endif -#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1) -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 4 -#endif -#if MG_UECC_SUPPORTS_secp384r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 6 -#endif -#endif /* MG_UECC_WORD_SIZE */ - -#define BITS_TO_WORDS(num_bits) \ - ((wordcount_t) ((num_bits + ((MG_UECC_WORD_SIZE * 8) - 1)) / \ - (MG_UECC_WORD_SIZE * 8))) -#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) - -struct MG_UECC_Curve_t { - wordcount_t num_words; - wordcount_t num_bytes; - bitcount_t num_n_bits; - mg_uecc_word_t p[MG_UECC_MAX_WORDS]; - mg_uecc_word_t n[MG_UECC_MAX_WORDS]; - mg_uecc_word_t G[MG_UECC_MAX_WORDS * 2]; - mg_uecc_word_t b[MG_UECC_MAX_WORDS]; - void (*double_jacobian)(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve); -#if MG_UECC_SUPPORT_COMPRESSED_POINT - void (*mod_sqrt)(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif - void (*x_side)(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - void (*mmod_fast)(mg_uecc_word_t *result, mg_uecc_word_t *product); -#endif -}; - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN -static void bcopy(uint8_t *dst, const uint8_t *src, unsigned num_bytes) { - while (0 != num_bytes) { - num_bytes--; - dst[num_bytes] = src[num_bytes]; - } -} -#endif - -static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -#if (MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) - -#endif - -#if (MG_UECC_PLATFORM == mg_uecc_avr) - -#endif - -#ifndef asm_clear -#define asm_clear 0 -#endif -#ifndef asm_set -#define asm_set 0 -#endif -#ifndef asm_add -#define asm_add 0 -#endif -#ifndef asm_sub -#define asm_sub 0 -#endif -#ifndef asm_mult -#define asm_mult 0 -#endif -#ifndef asm_rshift1 -#define asm_rshift1 0 -#endif -#ifndef asm_mmod_fast_secp256r1 -#define asm_mmod_fast_secp256r1 0 -#endif - -static MG_UECC_RNG_Function g_rng_function = NULL; - -void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function) { - g_rng_function = rng_function; -} - -MG_UECC_RNG_Function mg_uecc_get_rng(void) { - return g_rng_function; -} - -int mg_uecc_curve_private_key_size(MG_UECC_Curve curve) { - return BITS_TO_BYTES(curve->num_n_bits); -} - -int mg_uecc_curve_public_key_size(MG_UECC_Curve curve) { - return 2 * curve->num_bytes; -} - -#if !asm_clear -MG_UECC_VLI_API void mg_uecc_vli_clear(mg_uecc_word_t *vli, - wordcount_t num_words) { - wordcount_t i; - for (i = 0; i < num_words; ++i) { - vli[i] = 0; - } -} -#endif /* !asm_clear */ - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, - wordcount_t num_words) { - mg_uecc_word_t bits = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - bits |= vli[i]; - } - return (bits == 0); -} - -/* Returns nonzero if bit 'bit' of vli is set. */ -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, - bitcount_t bit) { - return (vli[bit >> MG_UECC_WORD_BITS_SHIFT] & - ((mg_uecc_word_t) 1 << (bit & MG_UECC_WORD_BITS_MASK))); -} - -/* Counts the number of words in vli. */ -static wordcount_t vli_numDigits(const mg_uecc_word_t *vli, - const wordcount_t max_words) { - wordcount_t i; - /* Search from the end until we find a non-zero digit. - We do it in reverse because we expect that most digits will be nonzero. */ - for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { - } - - return (i + 1); -} - -/* Counts the number of bits required to represent vli. */ -MG_UECC_VLI_API bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, - const wordcount_t max_words) { - mg_uecc_word_t i; - mg_uecc_word_t digit; - - wordcount_t num_digits = vli_numDigits(vli, max_words); - if (num_digits == 0) { - return 0; - } - - digit = vli[num_digits - 1]; - for (i = 0; digit; ++i) { - digit >>= 1; - } - - return (((bitcount_t) ((num_digits - 1) << MG_UECC_WORD_BITS_SHIFT)) + - (bitcount_t) i); -} - -/* Sets dest = src. */ -#if !asm_set -MG_UECC_VLI_API void mg_uecc_vli_set(mg_uecc_word_t *dest, - const mg_uecc_word_t *src, - wordcount_t num_words) { - wordcount_t i; - for (i = 0; i < num_words; ++i) { - dest[i] = src[i]; - } -} -#endif /* !asm_set */ - -/* Returns sign of left - right. */ -static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - wordcount_t i; - for (i = num_words - 1; i >= 0; --i) { - if (left[i] > right[i]) { - return 1; - } else if (left[i] < right[i]) { - return -1; - } - } - return 0; -} - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise. */ -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t diff = 0; - wordcount_t i; - for (i = num_words - 1; i >= 0; --i) { - diff |= (left[i] ^ right[i]); - } - return (diff == 0); -} - -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Returns sign of left - right, in constant time. */ -MG_UECC_VLI_API cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t tmp[MG_UECC_MAX_WORDS]; - mg_uecc_word_t neg = !!mg_uecc_vli_sub(tmp, left, right, num_words); - mg_uecc_word_t equal = mg_uecc_vli_isZero(tmp, num_words); - return (cmpresult_t) (!equal - 2 * neg); -} - -/* Computes vli = vli >> 1. */ -#if !asm_rshift1 -MG_UECC_VLI_API void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, - wordcount_t num_words) { - mg_uecc_word_t *end = vli; - mg_uecc_word_t carry = 0; - - vli += num_words; - while (vli-- > end) { - mg_uecc_word_t temp = *vli; - *vli = (temp >> 1) | carry; - carry = temp << (MG_UECC_WORD_BITS - 1); - } -} -#endif /* !asm_rshift1 */ - -/* Computes result = left + right, returning carry. Can modify in place. */ -#if !asm_add -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t carry = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - mg_uecc_word_t sum = left[i] + right[i] + carry; - if (sum != left[i]) { - carry = (sum < left[i]); - } - result[i] = sum; - } - return carry; -} -#endif /* !asm_add */ - -/* Computes result = left - right, returning borrow. Can modify in place. */ -#if !asm_sub -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t borrow = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - mg_uecc_word_t diff = left[i] - right[i] - borrow; - if (diff != left[i]) { - borrow = (diff > left[i]); - } - result[i] = diff; - } - return borrow; -} -#endif /* !asm_sub */ - -#if !asm_mult || (MG_UECC_SQUARE_FUNC && !asm_square) || \ - (MG_UECC_SUPPORTS_secp256k1 && (MG_UECC_OPTIMIZATION_LEVEL > 0) && \ - ((MG_UECC_WORD_SIZE == 1) || (MG_UECC_WORD_SIZE == 8))) -static void muladd(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0, - mg_uecc_word_t *r1, mg_uecc_word_t *r2) { -#if MG_UECC_WORD_SIZE == 8 - uint64_t a0 = a & 0xffffffff; - uint64_t a1 = a >> 32; - uint64_t b0 = b & 0xffffffff; - uint64_t b1 = b >> 32; - - uint64_t i0 = a0 * b0; - uint64_t i1 = a0 * b1; - uint64_t i2 = a1 * b0; - uint64_t i3 = a1 * b1; - - uint64_t p0, p1; - - i2 += (i0 >> 32); - i2 += i1; - if (i2 < i1) { /* overflow */ - i3 += 0x100000000; - } - - p0 = (i0 & 0xffffffff) | (i2 << 32); - p1 = i3 + (i2 >> 32); - - *r0 += p0; - *r1 += (p1 + (*r0 < p0)); - *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); -#else - mg_uecc_dword_t p = (mg_uecc_dword_t) a * b; - mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0; - r01 += p; - *r2 += (r01 < p); - *r1 = (mg_uecc_word_t) (r01 >> MG_UECC_WORD_BITS); - *r0 = (mg_uecc_word_t) r01; -#endif -} -#endif /* muladd needed */ - -#if !asm_mult -MG_UECC_VLI_API void mg_uecc_vli_mult(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - wordcount_t i, k; - - /* Compute each digit of result in sequence, maintaining the carries. */ - for (k = 0; k < num_words; ++k) { - for (i = 0; i <= k; ++i) { - muladd(left[i], right[k - i], &r0, &r1, &r2); - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - for (k = num_words; k < num_words * 2 - 1; ++k) { - for (i = (wordcount_t) ((k + 1) - num_words); i < num_words; ++i) { - muladd(left[i], right[k - i], &r0, &r1, &r2); - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - result[num_words * 2 - 1] = r0; -} -#endif /* !asm_mult */ - -#if MG_UECC_SQUARE_FUNC - -#if !asm_square -static void mul2add(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0, - mg_uecc_word_t *r1, mg_uecc_word_t *r2) { -#if MG_UECC_WORD_SIZE == 8 - uint64_t a0 = a & 0xffffffffull; - uint64_t a1 = a >> 32; - uint64_t b0 = b & 0xffffffffull; - uint64_t b1 = b >> 32; - - uint64_t i0 = a0 * b0; - uint64_t i1 = a0 * b1; - uint64_t i2 = a1 * b0; - uint64_t i3 = a1 * b1; - - uint64_t p0, p1; - - i2 += (i0 >> 32); - i2 += i1; - if (i2 < i1) { /* overflow */ - i3 += 0x100000000ull; - } - - p0 = (i0 & 0xffffffffull) | (i2 << 32); - p1 = i3 + (i2 >> 32); - - *r2 += (p1 >> 63); - p1 = (p1 << 1) | (p0 >> 63); - p0 <<= 1; - - *r0 += p0; - *r1 += (p1 + (*r0 < p0)); - *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); -#else - mg_uecc_dword_t p = (mg_uecc_dword_t) a * b; - mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0; - *r2 += (p >> (MG_UECC_WORD_BITS * 2 - 1)); - p *= 2; - r01 += p; - *r2 += (r01 < p); - *r1 = r01 >> MG_UECC_WORD_BITS; - *r0 = (mg_uecc_word_t) r01; -#endif -} - -MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - wordcount_t num_words) { - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - - wordcount_t i, k; - - for (k = 0; k < num_words * 2 - 1; ++k) { - mg_uecc_word_t min = (k < num_words ? 0 : (k + 1) - num_words); - for (i = min; i <= k && i <= k - i; ++i) { - if (i < k - i) { - mul2add(left[i], left[k - i], &r0, &r1, &r2); - } else { - muladd(left[i], left[k - i], &r0, &r1, &r2); - } - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - - result[num_words * 2 - 1] = r0; -} -#endif /* !asm_square */ - -#else /* MG_UECC_SQUARE_FUNC */ - -#if MG_UECC_ENABLE_VLI_API -MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - wordcount_t num_words) { - mg_uecc_vli_mult(result, left, left, num_words); -} -#endif /* MG_UECC_ENABLE_VLI_API */ - -#endif /* MG_UECC_SQUARE_FUNC */ - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modAdd(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t carry = mg_uecc_vli_add(result, left, right, num_words); - if (carry || mg_uecc_vli_cmp_unsafe(mod, result, num_words) != 1) { - /* result > mod (result = mod + remainder), so subtract mod to get - * remainder. */ - mg_uecc_vli_sub(result, result, mod, num_words); - } -} - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modSub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t l_borrow = mg_uecc_vli_sub(result, left, right, num_words); - if (l_borrow) { - /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, - we can get the correct result from result + mod (with overflow). */ - mg_uecc_vli_add(result, result, mod, num_words); - } -} - -/* Computes result = product % mod, where product is 2N words long. */ -/* Currently only designed to work for curve_p or curve_n. */ -MG_UECC_VLI_API void mg_uecc_vli_mmod(mg_uecc_word_t *result, - mg_uecc_word_t *product, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t mod_multiple[2 * MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp[2 * MG_UECC_MAX_WORDS]; - mg_uecc_word_t *v[2] = {tmp, product}; - mg_uecc_word_t index; - - /* Shift mod so its highest set bit is at the maximum position. */ - bitcount_t shift = (bitcount_t) ((num_words * 2 * MG_UECC_WORD_BITS) - - mg_uecc_vli_numBits(mod, num_words)); - wordcount_t word_shift = (wordcount_t) (shift / MG_UECC_WORD_BITS); - wordcount_t bit_shift = (wordcount_t) (shift % MG_UECC_WORD_BITS); - mg_uecc_word_t carry = 0; - mg_uecc_vli_clear(mod_multiple, word_shift); - if (bit_shift > 0) { - for (index = 0; index < (mg_uecc_word_t) num_words; ++index) { - mod_multiple[(mg_uecc_word_t) word_shift + index] = - (mg_uecc_word_t) (mod[index] << bit_shift) | carry; - carry = mod[index] >> (MG_UECC_WORD_BITS - bit_shift); - } - } else { - mg_uecc_vli_set(mod_multiple + word_shift, mod, num_words); - } - - for (index = 1; shift >= 0; --shift) { - mg_uecc_word_t borrow = 0; - wordcount_t i; - for (i = 0; i < num_words * 2; ++i) { - mg_uecc_word_t diff = v[index][i] - mod_multiple[i] - borrow; - if (diff != v[index][i]) { - borrow = (diff > v[index][i]); - } - v[1 - index][i] = diff; - } - index = !(index ^ borrow); /* Swap the index if there was no borrow */ - mg_uecc_vli_rshift1(mod_multiple, num_words); - mod_multiple[num_words - 1] |= mod_multiple[num_words] - << (MG_UECC_WORD_BITS - 1); - mg_uecc_vli_rshift1(mod_multiple + num_words, num_words); - } - mg_uecc_vli_set(result, v[index], num_words); -} - -/* Computes result = (left * right) % mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modMult(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_mult(product, left, right, num_words); - mg_uecc_vli_mmod(result, product, mod, num_words); -} - -MG_UECC_VLI_API void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - MG_UECC_Curve curve) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_mult(product, left, right, curve->num_words); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - mg_uecc_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -#if MG_UECC_SQUARE_FUNC - -#if MG_UECC_ENABLE_VLI_API -/* Computes result = left^2 % mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_square(product, left, num_words); - mg_uecc_vli_mmod(result, product, mod, num_words); -} -#endif /* MG_UECC_ENABLE_VLI_API */ - -MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_square(product, left, curve->num_words); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - mg_uecc_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -#else /* MG_UECC_SQUARE_FUNC */ - -#if MG_UECC_ENABLE_VLI_API -MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_vli_modMult(result, left, left, mod, num_words); -} -#endif /* MG_UECC_ENABLE_VLI_API */ - -MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve) { - mg_uecc_vli_modMult_fast(result, left, left, curve); -} - -#endif /* MG_UECC_SQUARE_FUNC */ - -#define EVEN(vli) (!(vli[0] & 1)) -static void vli_modInv_update(mg_uecc_word_t *uv, const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t carry = 0; - if (!EVEN(uv)) { - carry = mg_uecc_vli_add(uv, uv, mod, num_words); - } - mg_uecc_vli_rshift1(uv, num_words); - if (carry) { - uv[num_words - 1] |= HIGH_BIT_SET; - } -} - -/* Computes result = (1 / input) % mod. All VLIs are the same size. - See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */ -MG_UECC_VLI_API void mg_uecc_vli_modInv(mg_uecc_word_t *result, - const mg_uecc_word_t *input, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t a[MG_UECC_MAX_WORDS], b[MG_UECC_MAX_WORDS], - u[MG_UECC_MAX_WORDS], v[MG_UECC_MAX_WORDS]; - cmpresult_t cmpResult; - - if (mg_uecc_vli_isZero(input, num_words)) { - mg_uecc_vli_clear(result, num_words); - return; - } - - mg_uecc_vli_set(a, input, num_words); - mg_uecc_vli_set(b, mod, num_words); - mg_uecc_vli_clear(u, num_words); - u[0] = 1; - mg_uecc_vli_clear(v, num_words); - while ((cmpResult = mg_uecc_vli_cmp_unsafe(a, b, num_words)) != 0) { - if (EVEN(a)) { - mg_uecc_vli_rshift1(a, num_words); - vli_modInv_update(u, mod, num_words); - } else if (EVEN(b)) { - mg_uecc_vli_rshift1(b, num_words); - vli_modInv_update(v, mod, num_words); - } else if (cmpResult > 0) { - mg_uecc_vli_sub(a, a, b, num_words); - mg_uecc_vli_rshift1(a, num_words); - if (mg_uecc_vli_cmp_unsafe(u, v, num_words) < 0) { - mg_uecc_vli_add(u, u, mod, num_words); - } - mg_uecc_vli_sub(u, u, v, num_words); - vli_modInv_update(u, mod, num_words); - } else { - mg_uecc_vli_sub(b, b, a, num_words); - mg_uecc_vli_rshift1(b, num_words); - if (mg_uecc_vli_cmp_unsafe(v, u, num_words) < 0) { - mg_uecc_vli_add(v, v, mod, num_words); - } - mg_uecc_vli_sub(v, v, u, num_words); - vli_modInv_update(v, mod, num_words); - } - } - mg_uecc_vli_set(result, u, num_words); -} - -/* ------ Point operations ------ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_CURVE_SPECIFIC_H_ -#define _UECC_CURVE_SPECIFIC_H_ - -#define num_bytes_secp160r1 20 -#define num_bytes_secp192r1 24 -#define num_bytes_secp224r1 28 -#define num_bytes_secp256r1 32 -#define num_bytes_secp256k1 32 -#define num_bytes_secp384r1 48 - -#if (MG_UECC_WORD_SIZE == 1) - -#define num_words_secp160r1 20 -#define num_words_secp192r1 24 -#define num_words_secp224r1 28 -#define num_words_secp256r1 32 -#define num_words_secp256k1 32 -#define num_words_secp384r1 48 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \ - 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d - -#elif (MG_UECC_WORD_SIZE == 4) - -#define num_words_secp160r1 5 -#define num_words_secp192r1 6 -#define num_words_secp224r1 7 -#define num_words_secp256r1 8 -#define num_words_secp256k1 8 -#define num_words_secp384r1 12 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a - -#elif (MG_UECC_WORD_SIZE == 8) - -#define num_words_secp160r1 3 -#define num_words_secp192r1 3 -#define num_words_secp224r1 4 -#define num_words_secp256r1 4 -#define num_words_secp256k1 4 -#define num_words_secp384r1 6 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##U -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##U - -#endif /* MG_UECC_WORD_SIZE */ - -#if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \ - MG_UECC_SUPPORTS_secp224r1 || MG_UECC_SUPPORTS_secp256r1 || \ - MG_UECC_SUPPORTS_secp384r1 -static void double_jacobian_default(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve) { - /* t1 = X, t2 = Y, t3 = Z */ - mg_uecc_word_t t4[MG_UECC_MAX_WORDS]; - mg_uecc_word_t t5[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - if (mg_uecc_vli_isZero(Z1, num_words)) { - return; - } - - mg_uecc_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ - mg_uecc_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ - mg_uecc_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ - mg_uecc_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ - mg_uecc_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ - - mg_uecc_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ - mg_uecc_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ - mg_uecc_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ - mg_uecc_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ - - mg_uecc_vli_modAdd(Z1, X1, X1, curve->p, - num_words); /* t3 = 2*(x1^2 - z1^4) */ - mg_uecc_vli_modAdd(X1, X1, Z1, curve->p, - num_words); /* t1 = 3*(x1^2 - z1^4) */ - if (mg_uecc_vli_testBit(X1, 0)) { - mg_uecc_word_t l_carry = mg_uecc_vli_add(X1, X1, curve->p, num_words); - mg_uecc_vli_rshift1(X1, num_words); - X1[num_words - 1] |= l_carry << (MG_UECC_WORD_BITS - 1); - } else { - mg_uecc_vli_rshift1(X1, num_words); - } - /* t1 = 3/2*(x1^2 - z1^4) = B */ - - mg_uecc_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ - mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ - mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ - mg_uecc_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ - mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ - mg_uecc_vli_modSub(t4, X1, t4, curve->p, - num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ - - mg_uecc_vli_set(X1, Z1, num_words); - mg_uecc_vli_set(Z1, Y1, num_words); - mg_uecc_vli_set(Y1, t4, num_words); -} - -/* Computes result = x^3 + ax + b. result must not overlap x. */ -static void x_side_default(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve) { - mg_uecc_word_t _3[MG_UECC_MAX_WORDS] = {3}; /* -a = 3 */ - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */ - mg_uecc_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ - mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ - mg_uecc_vli_modAdd(result, result, curve->b, curve->p, - num_words); /* r = x^3 - 3x + b */ -} -#endif /* MG_UECC_SUPPORTS_secp... */ - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -#if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \ - MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1 || \ - MG_UECC_SUPPORTS_secp384r1 -/* Compute a = sqrt(a) (mod curve_p). */ -static void mod_sqrt_default(mg_uecc_word_t *a, MG_UECC_Curve curve) { - bitcount_t i; - mg_uecc_word_t p1[MG_UECC_MAX_WORDS] = {1}; - mg_uecc_word_t l_result[MG_UECC_MAX_WORDS] = {1}; - wordcount_t num_words = curve->num_words; - - /* When curve->p == 3 (mod 4), we can compute - sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */ - mg_uecc_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ - for (i = mg_uecc_vli_numBits(p1, num_words) - 1; i > 1; --i) { - mg_uecc_vli_modSquare_fast(l_result, l_result, curve); - if (mg_uecc_vli_testBit(p1, i)) { - mg_uecc_vli_modMult_fast(l_result, l_result, a, curve); - } - } - mg_uecc_vli_set(a, l_result, num_words); -} -#endif /* MG_UECC_SUPPORTS_secp... */ -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -#if MG_UECC_SUPPORTS_secp160r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp160r1 = { - num_words_secp160r1, - num_bytes_secp160r1, - 161, /* num_n_bits */ - {BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9), - BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00)}, - {BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68), - BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E), - BYTES_TO_WORDS_4(68, B5, 96, 4A), - - BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04), - BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31), - BYTES_TO_WORDS_4(55, 28, A6, 23)}, - {BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81), - BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54), - BYTES_TO_WORDS_4(FC, BE, 97, 1C)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp160r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp160r1(void) { - return &curve_secp160r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) -/* Computes result = product % curve_p - see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354 - - Note that this only works if log2(omega) < log2(p) / 2 */ -static void omega_mult_secp160r1(mg_uecc_word_t *result, - const mg_uecc_word_t *right); -#if MG_UECC_WORD_SIZE == 8 -static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_word_t tmp[2 * num_words_secp160r1]; - mg_uecc_word_t copy; - - mg_uecc_vli_clear(tmp, num_words_secp160r1); - mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); - - omega_mult_secp160r1(tmp, - product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */ - - product[num_words_secp160r1 - 1] &= 0xffffffff; - copy = tmp[num_words_secp160r1 - 1]; - tmp[num_words_secp160r1 - 1] &= 0xffffffff; - mg_uecc_vli_add(result, product, tmp, - num_words_secp160r1); /* (C, r) = r + q */ - mg_uecc_vli_clear(product, num_words_secp160r1); - tmp[num_words_secp160r1 - 1] = copy; - omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */ - mg_uecc_vli_add(result, result, product, - num_words_secp160r1); /* (C1, r) = r + Rq*c */ - - while (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p, - num_words_secp160r1) > 0) { - mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } -} - -static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) { - uint32_t carry; - unsigned i; - - /* Multiply by (2^31 + 1). */ - carry = 0; - for (i = 0; i < num_words_secp160r1; ++i) { - uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32); - result[i] = (tmp << 31) + tmp + carry; - carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp)); - } - result[i] = carry; -} -#else -static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_word_t tmp[2 * num_words_secp160r1]; - mg_uecc_word_t carry; - - mg_uecc_vli_clear(tmp, num_words_secp160r1); - mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); - - omega_mult_secp160r1(tmp, - product + num_words_secp160r1); /* (Rq, q) = q * c */ - - carry = mg_uecc_vli_add(result, product, tmp, - num_words_secp160r1); /* (C, r) = r + q */ - mg_uecc_vli_clear(product, num_words_secp160r1); - omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */ - carry += mg_uecc_vli_add(result, result, product, - num_words_secp160r1); /* (C1, r) = r + Rq*c */ - - while (carry > 0) { - --carry; - mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } - if (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > - 0) { - mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } -} -#endif - -#if MG_UECC_WORD_SIZE == 1 -static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) { - uint8_t carry; - uint8_t i; - - /* Multiply by (2^31 + 1). */ - mg_uecc_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */ - mg_uecc_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */ - result[3] = right[0] << 7; /* get last bit from shift */ - - carry = mg_uecc_vli_add(result, result, right, - num_words_secp160r1); /* 2^31 + 1 */ - for (i = num_words_secp160r1; carry; ++i) { - uint16_t sum = (uint16_t) result[i] + carry; - result[i] = (uint8_t) sum; - carry = sum >> 8; - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) { - uint32_t carry; - unsigned i; - - /* Multiply by (2^31 + 1). */ - mg_uecc_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */ - mg_uecc_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */ - result[0] = right[0] << 31; /* get last bit from shift */ - - carry = mg_uecc_vli_add(result, result, right, - num_words_secp160r1); /* 2^31 + 1 */ - for (i = num_words_secp160r1; carry; ++i) { - uint64_t sum = (uint64_t) result[i] + carry; - result[i] = (uint32_t) sum; - carry = sum >> 32; - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */ - -#endif /* MG_UECC_SUPPORTS_secp160r1 */ - -#if MG_UECC_SUPPORTS_secp192r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp192r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp192r1 = { - num_words_secp192r1, - num_bytes_secp192r1, - 192, /* num_n_bits */ - {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14), - BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4), - BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C), - BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18), - - BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73), - BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63), - BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07)}, - {BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE), - BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F), - BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp192r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp192r1(void) { - return &curve_secp192r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -/* Computes result = product % curve_p. - See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf - */ -#if MG_UECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp192r1]; - uint8_t carry; - - mg_uecc_vli_set(result, product, num_words_secp192r1); - - mg_uecc_vli_set(tmp, &product[24], num_words_secp192r1); - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = product[24]; - tmp[9] = product[25]; - tmp[10] = product[26]; - tmp[11] = product[27]; - tmp[12] = product[28]; - tmp[13] = product[29]; - tmp[14] = product[30]; - tmp[15] = product[31]; - tmp[16] = product[32]; - tmp[17] = product[33]; - tmp[18] = product[34]; - tmp[19] = product[35]; - tmp[20] = product[36]; - tmp[21] = product[37]; - tmp[22] = product[38]; - tmp[23] = product[39]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[8] = product[40]; - tmp[1] = tmp[9] = product[41]; - tmp[2] = tmp[10] = product[42]; - tmp[3] = tmp[11] = product[43]; - tmp[4] = tmp[12] = product[44]; - tmp[5] = tmp[13] = product[45]; - tmp[6] = tmp[14] = product[46]; - tmp[7] = tmp[15] = product[47]; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] = - tmp[23] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result, - num_words_secp192r1) != 1) { - carry -= - mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp192r1]; - int carry; - - mg_uecc_vli_set(result, product, num_words_secp192r1); - - mg_uecc_vli_set(tmp, &product[6], num_words_secp192r1); - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = 0; - tmp[2] = product[6]; - tmp[3] = product[7]; - tmp[4] = product[8]; - tmp[5] = product[9]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[2] = product[10]; - tmp[1] = tmp[3] = product[11]; - tmp[4] = tmp[5] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result, - num_words_secp192r1) != 1) { - carry -= - mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#else -static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp192r1]; - int carry; - - mg_uecc_vli_set(result, product, num_words_secp192r1); - - mg_uecc_vli_set(tmp, &product[3], num_words_secp192r1); - carry = (int) mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = 0; - tmp[1] = product[3]; - tmp[2] = product[4]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = product[5]; - tmp[2] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result, - num_words_secp192r1) != 1) { - carry -= - mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */ - -#endif /* MG_UECC_SUPPORTS_secp192r1 */ - -#if MG_UECC_SUPPORTS_secp224r1 - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp224r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp224r1 = { - num_words_secp224r1, - num_bytes_secp224r1, - 224, /* num_n_bits */ - {BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13), - BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34), - BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A), - BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B), - BYTES_TO_WORDS_4(BD, 0C, 0E, B7), - - BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44), - BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD), - BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5), - BYTES_TO_WORDS_4(88, 63, 37, BD)}, - {BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27), - BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50), - BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C), - BYTES_TO_WORDS_4(85, 0A, 05, B4)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_secp224r1, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp224r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp224r1(void) { - return &curve_secp224r1; -} - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rs(mg_uecc_word_t *d1, mg_uecc_word_t *e1, - mg_uecc_word_t *f1, const mg_uecc_word_t *d0, - const mg_uecc_word_t *e0, - const mg_uecc_word_t *f0) { - mg_uecc_word_t t[num_words_secp224r1]; - - mg_uecc_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */ - mg_uecc_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */ - mg_uecc_vli_modAdd(d1, t, f0, curve_secp224r1.p, - num_words_secp224r1); /* d1 <-- t + f0 */ - mg_uecc_vli_modAdd(e1, e1, e1, curve_secp224r1.p, - num_words_secp224r1); /* e1 <-- e1 + e1 */ - mg_uecc_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */ - mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p, - num_words_secp224r1); /* f1 <-- f1 + f1 */ - mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p, - num_words_secp224r1); /* f1 <-- f1 + f1 */ -} - -/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rss(mg_uecc_word_t *d1, mg_uecc_word_t *e1, - mg_uecc_word_t *f1, const mg_uecc_word_t *d0, - const mg_uecc_word_t *e0, - const mg_uecc_word_t *f0, - const bitcount_t j) { - bitcount_t i; - - mg_uecc_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */ - mg_uecc_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */ - mg_uecc_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */ - for (i = 1; i <= j; i++) { - mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */ - } -} - -/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rm(mg_uecc_word_t *d2, mg_uecc_word_t *e2, - mg_uecc_word_t *f2, const mg_uecc_word_t *c, - const mg_uecc_word_t *d0, - const mg_uecc_word_t *e0, - const mg_uecc_word_t *d1, - const mg_uecc_word_t *e1) { - mg_uecc_word_t t1[num_words_secp224r1]; - mg_uecc_word_t t2[num_words_secp224r1]; - - mg_uecc_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */ - mg_uecc_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */ - /* t1 <-- p - t1 */ - mg_uecc_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p, - num_words_secp224r1); - mg_uecc_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */ - mg_uecc_vli_modAdd(t2, t2, t1, curve_secp224r1.p, - num_words_secp224r1); /* t2 <-- t2 + t1 */ - mg_uecc_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */ - mg_uecc_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */ - mg_uecc_vli_modAdd(e2, e2, t1, curve_secp224r1.p, - num_words_secp224r1); /* e2 <-- e2 + t1 */ - mg_uecc_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */ - mg_uecc_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */ - /* f2 <-- p - f2 */ - mg_uecc_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p, - num_words_secp224r1); - mg_uecc_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */ -} - -/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rp(mg_uecc_word_t *d1, mg_uecc_word_t *e1, - mg_uecc_word_t *f1, const mg_uecc_word_t *c, - const mg_uecc_word_t *r) { - wordcount_t i; - wordcount_t pow2i = 1; - mg_uecc_word_t d0[num_words_secp224r1]; - mg_uecc_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */ - mg_uecc_word_t f0[num_words_secp224r1]; - - mg_uecc_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */ - /* f0 <-- p - c */ - mg_uecc_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p, - num_words_secp224r1); - for (i = 0; i <= 6; i++) { - mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0, - pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */ - mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0, - e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */ - mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ - mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ - mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ - pow2i *= 2; - } -} - -/* Compute a = sqrt(a) (mod curve_p). */ -/* Routine 3.2.8 mp_mod_sqrt_224; from - * http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve) { - (void) curve; - bitcount_t i; - mg_uecc_word_t e1[num_words_secp224r1]; - mg_uecc_word_t f1[num_words_secp224r1]; - mg_uecc_word_t d0[num_words_secp224r1]; - mg_uecc_word_t e0[num_words_secp224r1]; - mg_uecc_word_t f0[num_words_secp224r1]; - mg_uecc_word_t d1[num_words_secp224r1]; - - /* s = a; using constant instead of random value */ - mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */ - mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, - f0); /* RS (d1, e1, f1, d0, e0, f0) */ - for (i = 1; i <= 95; i++) { - mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ - mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ - mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ - mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, - f0); /* RS (d1, e1, f1, d0, e0, f0) */ - if (mg_uecc_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */ - break; - } - } - mg_uecc_vli_modInv(f1, e0, curve_secp224r1.p, - num_words_secp224r1); /* f1 <-- 1 / e0 */ - mg_uecc_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */ -} -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -#if MG_UECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp224r1]; - int8_t carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp224r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; - tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[28]; - tmp[13] = product[29]; - tmp[14] = product[30]; - tmp[15] = product[31]; - tmp[16] = product[32]; - tmp[17] = product[33]; - tmp[18] = product[34]; - tmp[19] = product[35]; - tmp[20] = product[36]; - tmp[21] = product[37]; - tmp[22] = product[38]; - tmp[23] = product[39]; - tmp[24] = product[40]; - tmp[25] = product[41]; - tmp[26] = product[42]; - tmp[27] = product[43]; - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[12] = product[44]; - tmp[13] = product[45]; - tmp[14] = product[46]; - tmp[15] = product[47]; - tmp[16] = product[48]; - tmp[17] = product[49]; - tmp[18] = product[50]; - tmp[19] = product[51]; - tmp[20] = product[52]; - tmp[21] = product[53]; - tmp[22] = product[54]; - tmp[23] = product[55]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = product[28]; - tmp[1] = product[29]; - tmp[2] = product[30]; - tmp[3] = product[31]; - tmp[4] = product[32]; - tmp[5] = product[33]; - tmp[6] = product[34]; - tmp[7] = product[35]; - tmp[8] = product[36]; - tmp[9] = product[37]; - tmp[10] = product[38]; - tmp[11] = product[39]; - tmp[12] = product[40]; - tmp[13] = product[41]; - tmp[14] = product[42]; - tmp[15] = product[43]; - tmp[16] = product[44]; - tmp[17] = product[45]; - tmp[18] = product[46]; - tmp[19] = product[47]; - tmp[20] = product[48]; - tmp[21] = product[49]; - tmp[22] = product[50]; - tmp[23] = product[51]; - tmp[24] = product[52]; - tmp[25] = product[53]; - tmp[26] = product[54]; - tmp[27] = product[55]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = product[44]; - tmp[1] = product[45]; - tmp[2] = product[46]; - tmp[3] = product[47]; - tmp[4] = product[48]; - tmp[5] = product[49]; - tmp[6] = product[50]; - tmp[7] = product[51]; - tmp[8] = product[52]; - tmp[9] = product[53]; - tmp[10] = product[54]; - tmp[11] = product[55]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp224r1.p, - num_words_secp224r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result, - num_words_secp224r1) != 1) { - carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p, - num_words_secp224r1); - } - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp224r1]; - int carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp224r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = 0; - tmp[3] = product[7]; - tmp[4] = product[8]; - tmp[5] = product[9]; - tmp[6] = product[10]; - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[3] = product[11]; - tmp[4] = product[12]; - tmp[5] = product[13]; - tmp[6] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = product[7]; - tmp[1] = product[8]; - tmp[2] = product[9]; - tmp[3] = product[10]; - tmp[4] = product[11]; - tmp[5] = product[12]; - tmp[6] = product[13]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = product[11]; - tmp[1] = product[12]; - tmp[2] = product[13]; - tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp224r1.p, - num_words_secp224r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result, - num_words_secp224r1) != 1) { - carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p, - num_words_secp224r1); - } - } -} -#else -static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp224r1]; - int carry = 0; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp224r1); - result[num_words_secp224r1 - 1] &= 0xffffffff; - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[3] & 0xffffffff00000000ull; - tmp[2] = product[4]; - tmp[3] = product[5] & 0xffffffff; - mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[1] = product[5] & 0xffffffff00000000ull; - tmp[2] = product[6]; - tmp[3] = 0; - mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = (product[3] >> 32) | (product[4] << 32); - tmp[1] = (product[4] >> 32) | (product[5] << 32); - tmp[2] = (product[5] >> 32) | (product[6] << 32); - tmp[3] = product[6] >> 32; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = product[6] >> 32; - tmp[2] = tmp[3] = 0; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp224r1.p, - num_words_secp224r1); - } while (carry < 0); - } else { - while (mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result, - num_words_secp224r1) != 1) { - mg_uecc_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); - } - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */ - -#endif /* MG_UECC_SUPPORTS_secp224r1 */ - -#if MG_UECC_SUPPORTS_secp256r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp256r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp256r1 = { - num_words_secp256r1, - num_bytes_secp256r1, - 256, /* num_n_bits */ - {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), - BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), - BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), - BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), - BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), - - BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), - BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), - BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), - BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F)}, - {BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), - BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), - BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), - BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp256r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp256r1(void) { - return &curve_secp256r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -#if MG_UECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp256r1]; - int8_t carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; - tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[44]; - tmp[13] = product[45]; - tmp[14] = product[46]; - tmp[15] = product[47]; - tmp[16] = product[48]; - tmp[17] = product[49]; - tmp[18] = product[50]; - tmp[19] = product[51]; - tmp[20] = product[52]; - tmp[21] = product[53]; - tmp[22] = product[54]; - tmp[23] = product[55]; - tmp[24] = product[56]; - tmp[25] = product[57]; - tmp[26] = product[58]; - tmp[27] = product[59]; - tmp[28] = product[60]; - tmp[29] = product[61]; - tmp[30] = product[62]; - tmp[31] = product[63]; - carry = mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[12] = product[48]; - tmp[13] = product[49]; - tmp[14] = product[50]; - tmp[15] = product[51]; - tmp[16] = product[52]; - tmp[17] = product[53]; - tmp[18] = product[54]; - tmp[19] = product[55]; - tmp[20] = product[56]; - tmp[21] = product[57]; - tmp[22] = product[58]; - tmp[23] = product[59]; - tmp[24] = product[60]; - tmp[25] = product[61]; - tmp[26] = product[62]; - tmp[27] = product[63]; - tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0; - carry += mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[32]; - tmp[1] = product[33]; - tmp[2] = product[34]; - tmp[3] = product[35]; - tmp[4] = product[36]; - tmp[5] = product[37]; - tmp[6] = product[38]; - tmp[7] = product[39]; - tmp[8] = product[40]; - tmp[9] = product[41]; - tmp[10] = product[42]; - tmp[11] = product[43]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[56]; - tmp[25] = product[57]; - tmp[26] = product[58]; - tmp[27] = product[59]; - tmp[28] = product[60]; - tmp[29] = product[61]; - tmp[30] = product[62]; - tmp[31] = product[63]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = product[36]; - tmp[1] = product[37]; - tmp[2] = product[38]; - tmp[3] = product[39]; - tmp[4] = product[40]; - tmp[5] = product[41]; - tmp[6] = product[42]; - tmp[7] = product[43]; - tmp[8] = product[44]; - tmp[9] = product[45]; - tmp[10] = product[46]; - tmp[11] = product[47]; - tmp[12] = product[52]; - tmp[13] = product[53]; - tmp[14] = product[54]; - tmp[15] = product[55]; - tmp[16] = product[56]; - tmp[17] = product[57]; - tmp[18] = product[58]; - tmp[19] = product[59]; - tmp[20] = product[60]; - tmp[21] = product[61]; - tmp[22] = product[62]; - tmp[23] = product[63]; - tmp[24] = product[52]; - tmp[25] = product[53]; - tmp[26] = product[54]; - tmp[27] = product[55]; - tmp[28] = product[32]; - tmp[29] = product[33]; - tmp[30] = product[34]; - tmp[31] = product[35]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = product[44]; - tmp[1] = product[45]; - tmp[2] = product[46]; - tmp[3] = product[47]; - tmp[4] = product[48]; - tmp[5] = product[49]; - tmp[6] = product[50]; - tmp[7] = product[51]; - tmp[8] = product[52]; - tmp[9] = product[53]; - tmp[10] = product[54]; - tmp[11] = product[55]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[32]; - tmp[25] = product[33]; - tmp[26] = product[34]; - tmp[27] = product[35]; - tmp[28] = product[40]; - tmp[29] = product[41]; - tmp[30] = product[42]; - tmp[31] = product[43]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[48]; - tmp[1] = product[49]; - tmp[2] = product[50]; - tmp[3] = product[51]; - tmp[4] = product[52]; - tmp[5] = product[53]; - tmp[6] = product[54]; - tmp[7] = product[55]; - tmp[8] = product[56]; - tmp[9] = product[57]; - tmp[10] = product[58]; - tmp[11] = product[59]; - tmp[12] = product[60]; - tmp[13] = product[61]; - tmp[14] = product[62]; - tmp[15] = product[63]; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[36]; - tmp[25] = product[37]; - tmp[26] = product[38]; - tmp[27] = product[39]; - tmp[28] = product[44]; - tmp[29] = product[45]; - tmp[30] = product[46]; - tmp[31] = product[47]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = product[52]; - tmp[1] = product[53]; - tmp[2] = product[54]; - tmp[3] = product[55]; - tmp[4] = product[56]; - tmp[5] = product[57]; - tmp[6] = product[58]; - tmp[7] = product[59]; - tmp[8] = product[60]; - tmp[9] = product[61]; - tmp[10] = product[62]; - tmp[11] = product[63]; - tmp[12] = product[32]; - tmp[13] = product[33]; - tmp[14] = product[34]; - tmp[15] = product[35]; - tmp[16] = product[36]; - tmp[17] = product[37]; - tmp[18] = product[38]; - tmp[19] = product[39]; - tmp[20] = product[40]; - tmp[21] = product[41]; - tmp[22] = product[42]; - tmp[23] = product[43]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - tmp[28] = product[48]; - tmp[29] = product[49]; - tmp[30] = product[50]; - tmp[31] = product[51]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[56]; - tmp[1] = product[57]; - tmp[2] = product[58]; - tmp[3] = product[59]; - tmp[4] = product[60]; - tmp[5] = product[61]; - tmp[6] = product[62]; - tmp[7] = product[63]; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[36]; - tmp[13] = product[37]; - tmp[14] = product[38]; - tmp[15] = product[39]; - tmp[16] = product[40]; - tmp[17] = product[41]; - tmp[18] = product[42]; - tmp[19] = product[43]; - tmp[20] = product[44]; - tmp[21] = product[45]; - tmp[22] = product[46]; - tmp[23] = product[47]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - tmp[28] = product[52]; - tmp[29] = product[53]; - tmp[30] = product[54]; - tmp[31] = product[55]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp256r1.p, - num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result, - num_words_secp256r1) != 1) { - carry -= mg_uecc_vli_sub(result, result, curve_secp256r1.p, - num_words_secp256r1); - } - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp256r1]; - int carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = 0; - tmp[3] = product[11]; - tmp[4] = product[12]; - tmp[5] = product[13]; - tmp[6] = product[14]; - tmp[7] = product[15]; - carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[3] = product[12]; - tmp[4] = product[13]; - tmp[5] = product[14]; - tmp[6] = product[15]; - tmp[7] = 0; - carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[8]; - tmp[1] = product[9]; - tmp[2] = product[10]; - tmp[3] = tmp[4] = tmp[5] = 0; - tmp[6] = product[14]; - tmp[7] = product[15]; - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = product[9]; - tmp[1] = product[10]; - tmp[2] = product[11]; - tmp[3] = product[13]; - tmp[4] = product[14]; - tmp[5] = product[15]; - tmp[6] = product[13]; - tmp[7] = product[8]; - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = product[11]; - tmp[1] = product[12]; - tmp[2] = product[13]; - tmp[3] = tmp[4] = tmp[5] = 0; - tmp[6] = product[8]; - tmp[7] = product[10]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[12]; - tmp[1] = product[13]; - tmp[2] = product[14]; - tmp[3] = product[15]; - tmp[4] = tmp[5] = 0; - tmp[6] = product[9]; - tmp[7] = product[11]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = product[13]; - tmp[1] = product[14]; - tmp[2] = product[15]; - tmp[3] = product[8]; - tmp[4] = product[9]; - tmp[5] = product[10]; - tmp[6] = 0; - tmp[7] = product[12]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[14]; - tmp[1] = product[15]; - tmp[2] = 0; - tmp[3] = product[9]; - tmp[4] = product[10]; - tmp[5] = product[11]; - tmp[6] = 0; - tmp[7] = product[13]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p, - num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result, - num_words_secp256r1) != 1) { - carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p, - num_words_secp256r1); - } - } -} -#else -static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp256r1]; - int carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[5] & 0xffffffff00000000U; - tmp[2] = product[6]; - tmp[3] = product[7]; - carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[1] = product[6] << 32; - tmp[2] = (product[6] >> 32) | (product[7] << 32); - tmp[3] = product[7] >> 32; - carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[4]; - tmp[1] = product[5] & 0xffffffff; - tmp[2] = 0; - tmp[3] = product[7]; - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = (product[4] >> 32) | (product[5] << 32); - tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000U); - tmp[2] = product[7]; - tmp[3] = (product[6] >> 32) | (product[4] << 32); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = (product[6] >> 32); - tmp[2] = 0; - tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[6]; - tmp[1] = product[7]; - tmp[2] = 0; - tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000); - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = (product[6] >> 32) | (product[7] << 32); - tmp[1] = (product[7] >> 32) | (product[4] << 32); - tmp[2] = (product[4] >> 32) | (product[5] << 32); - tmp[3] = (product[6] << 32); - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[7]; - tmp[1] = product[4] & 0xffffffff00000000U; - tmp[2] = product[5]; - tmp[3] = product[6] & 0xffffffff00000000U; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p, - num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result, - num_words_secp256r1) != 1) { - carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p, - num_words_secp256r1); - } - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */ - -#endif /* MG_UECC_SUPPORTS_secp256r1 */ - -#if MG_UECC_SUPPORTS_secp256k1 - -static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve); -static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp256k1 = { - num_words_secp256k1, - num_bytes_secp256k1, - 256, /* num_n_bits */ - {BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF), - BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59), - BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02), - BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55), - BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79), - - BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C), - BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD), - BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D), - BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48)}, - {BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00)}, - &double_jacobian_secp256k1, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_secp256k1, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp256k1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp256k1(void) { - return &curve_secp256k1; -} - -/* Double in place */ -static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve) { - /* t1 = X, t2 = Y, t3 = Z */ - mg_uecc_word_t t4[num_words_secp256k1]; - mg_uecc_word_t t5[num_words_secp256k1]; - - if (mg_uecc_vli_isZero(Z1, num_words_secp256k1)) { - return; - } - - mg_uecc_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ - mg_uecc_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ - mg_uecc_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ - mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ - mg_uecc_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ - - mg_uecc_vli_modAdd(Y1, X1, X1, curve->p, - num_words_secp256k1); /* t2 = 2*x1^2 */ - mg_uecc_vli_modAdd(Y1, Y1, X1, curve->p, - num_words_secp256k1); /* t2 = 3*x1^2 */ - if (mg_uecc_vli_testBit(Y1, 0)) { - mg_uecc_word_t carry = - mg_uecc_vli_add(Y1, Y1, curve->p, num_words_secp256k1); - mg_uecc_vli_rshift1(Y1, num_words_secp256k1); - Y1[num_words_secp256k1 - 1] |= carry << (MG_UECC_WORD_BITS - 1); - } else { - mg_uecc_vli_rshift1(Y1, num_words_secp256k1); - } - /* t2 = 3/2*(x1^2) = B */ - - mg_uecc_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ - mg_uecc_vli_modSub(X1, X1, t4, curve->p, - num_words_secp256k1); /* t1 = B^2 - A */ - mg_uecc_vli_modSub(X1, X1, t4, curve->p, - num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ - - mg_uecc_vli_modSub(t4, t4, X1, curve->p, - num_words_secp256k1); /* t4 = A - x3 */ - mg_uecc_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ - mg_uecc_vli_modSub(Y1, Y1, t5, curve->p, - num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ -} - -/* Computes result = x^3 + b. result must not overlap x. */ -static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve) { - mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */ - mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ - mg_uecc_vli_modAdd(result, result, curve->b, curve->p, - num_words_secp256k1); /* r = x^3 + b */ -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1) -static void omega_mult_secp256k1(mg_uecc_word_t *result, - const mg_uecc_word_t *right); -static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_word_t tmp[2 * num_words_secp256k1]; - mg_uecc_word_t carry; - - mg_uecc_vli_clear(tmp, num_words_secp256k1); - mg_uecc_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); - - omega_mult_secp256k1(tmp, - product + num_words_secp256k1); /* (Rq, q) = q * c */ - - carry = mg_uecc_vli_add(result, product, tmp, - num_words_secp256k1); /* (C, r) = r + q */ - mg_uecc_vli_clear(product, num_words_secp256k1); - omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ - carry += mg_uecc_vli_add(result, result, product, - num_words_secp256k1); /* (C1, r) = r + Rq*c */ - - while (carry > 0) { - --carry; - mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); - } - if (mg_uecc_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > - 0) { - mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); - } -} - -#if MG_UECC_WORD_SIZE == 1 -static void omega_mult_secp256k1(uint8_t *result, const uint8_t *right) { - /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - wordcount_t k; - - /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - muladd(0xD1, right[0], &r0, &r1, &r2); - result[0] = r0; - r0 = r1; - r1 = r2; - /* r2 is still 0 */ - - for (k = 1; k < num_words_secp256k1; ++k) { - muladd(0x03, right[k - 1], &r0, &r1, &r2); - muladd(0xD1, right[k], &r0, &r1, &r2); - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2); - result[num_words_secp256k1] = r0; - result[num_words_secp256k1 + 1] = r1; - /* add the 2^32 multiple */ - result[4 + num_words_secp256k1] = - mg_uecc_vli_add(result + 4, result + 4, right, num_words_secp256k1); -} -#elif MG_UECC_WORD_SIZE == 4 -static void omega_mult_secp256k1(uint32_t *result, const uint32_t *right) { - /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - uint32_t carry = 0; - wordcount_t k; - - for (k = 0; k < num_words_secp256k1; ++k) { - uint64_t p = (uint64_t) 0x3D1 * right[k] + carry; - result[k] = (uint32_t) p; - carry = p >> 32; - } - result[num_words_secp256k1] = carry; - /* add the 2^32 multiple */ - result[1 + num_words_secp256k1] = - mg_uecc_vli_add(result + 1, result + 1, right, num_words_secp256k1); -} -#else -static void omega_mult_secp256k1(uint64_t *result, const uint64_t *right) { - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - wordcount_t k; - - /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - for (k = 0; k < num_words_secp256k1; ++k) { - muladd(0x1000003D1ull, right[k], &r0, &r1, &r2); - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - result[num_words_secp256k1] = r0; -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */ - -#endif /* MG_UECC_SUPPORTS_secp256k1 */ - -#if MG_UECC_SUPPORTS_secp384r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp384r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp384r1 = { - num_words_secp384r1, - num_bytes_secp384r1, - 384, /* num_n_bits */ - /* p */ - {BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - /* n */ - {BYTES_TO_WORDS_8(73, 29, C5, CC, 6A, 19, EC, EC), - BYTES_TO_WORDS_8(7A, A7, B0, 48, B2, 0D, 1A, 58), - BYTES_TO_WORDS_8(DF, 2D, 37, F4, 81, 4D, 63, C7), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - /* G = (Gx || Gy) little-endian */ - {BYTES_TO_WORDS_8(B7, 0A, 76, 72, 38, 5E, 54, 3A), - BYTES_TO_WORDS_8(6C, 29, 55, BF, 5D, F2, 02, 55), - BYTES_TO_WORDS_8(38, 2A, 54, 82, E0, 41, F7, 59), - BYTES_TO_WORDS_8(98, 9B, A7, 8B, 62, 3B, 1D, 6E), - BYTES_TO_WORDS_8(74, AD, 20, F3, 1E, C7, B1, 8E), - BYTES_TO_WORDS_8(37, 05, 8B, BE, 22, CA, 87, AA), - - BYTES_TO_WORDS_8(5F, 0E, EA, 90, 7C, 1D, 43, 7A), - BYTES_TO_WORDS_8(9D, 81, 7E, 1D, CE, B1, 60, 0A), - BYTES_TO_WORDS_8(C0, B8, F0, B5, 13, 31, DA, E9), - BYTES_TO_WORDS_8(7C, 14, 9A, 28, BD, 1D, F4, F8), - BYTES_TO_WORDS_8(29, DC, 92, 92, BF, 98, 9E, 5D), - BYTES_TO_WORDS_8(6F, 2C, 26, 96, 4A, DE, 17, 36)}, - /* b */ - {BYTES_TO_WORDS_8(EF, 2A, EC, D3, ED, C8, 85, 2A), - BYTES_TO_WORDS_8(9D, D1, 2E, 8A, 8D, 39, 56, C6), - BYTES_TO_WORDS_8(5A, 87, 13, 50, 8F, 08, 14, 03), - BYTES_TO_WORDS_8(12, 41, 81, FE, 6E, 9C, 1D, 18), - BYTES_TO_WORDS_8(19, 2D, F8, E3, 6B, 05, 8E, 98), - BYTES_TO_WORDS_8(E4, E7, 3E, E2, A7, 2F, 31, B3)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp384r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp384r1(void) { - return &curve_secp384r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp384r1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_vli_mmod(result, product, curve_secp384r1.p, num_words_secp384r1); -} -#endif - -#endif /* MG_UECC_SUPPORTS_secp384r1 */ - -#endif /* _UECC_CURVE_SPECIFIC_H_ */ - -/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */ -#define EccPoint_isZero(point, curve) \ - mg_uecc_vli_isZero((point), (wordcount_t) ((curve)->num_words * 2)) - -/* Point multiplication algorithm using Montgomery's ladder with co-Z -coordinates. From http://eprint.iacr.org/2011/338.pdf -*/ - -/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ -static void apply_z(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - const mg_uecc_word_t *const Z, MG_UECC_Curve curve) { - mg_uecc_word_t t1[MG_UECC_MAX_WORDS]; - - mg_uecc_vli_modSquare_fast(t1, Z, curve); /* z^2 */ - mg_uecc_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ - mg_uecc_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ - mg_uecc_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ -} - -/* P = (x1, y1) => 2P, (x2, y2) => P' */ -static void XYcZ_initial_double(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *X2, mg_uecc_word_t *Y2, - const mg_uecc_word_t *const initial_Z, - MG_UECC_Curve curve) { - mg_uecc_word_t z[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - if (initial_Z) { - mg_uecc_vli_set(z, initial_Z, num_words); - } else { - mg_uecc_vli_clear(z, num_words); - z[0] = 1; - } - - mg_uecc_vli_set(X2, X1, num_words); - mg_uecc_vli_set(Y2, Y1, num_words); - - apply_z(X1, Y1, z, curve); - curve->double_jacobian(X1, Y1, z, curve); - apply_z(X2, Y2, z, curve); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) - or P => P', Q => P + Q -*/ -static void XYcZ_add(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, mg_uecc_word_t *X2, - mg_uecc_word_t *Y2, MG_UECC_Curve curve) { - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0}; - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ - mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ - mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ - mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ - mg_uecc_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ - - mg_uecc_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ - mg_uecc_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ - mg_uecc_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ - mg_uecc_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ - mg_uecc_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ - mg_uecc_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ - - mg_uecc_vli_set(X2, t5, num_words); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) - or P => P - Q, Q => P + Q -*/ -static void XYcZ_addC(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *X2, mg_uecc_word_t *Y2, - MG_UECC_Curve curve) { - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0}; - mg_uecc_word_t t6[MG_UECC_MAX_WORDS]; - mg_uecc_word_t t7[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ - mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ - mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ - mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ - mg_uecc_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ - - mg_uecc_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ - mg_uecc_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ - mg_uecc_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ - mg_uecc_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ - mg_uecc_vli_modSub(X2, X2, t6, curve->p, - num_words); /* t3 = D - (B + C) = x3 */ - - mg_uecc_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ - mg_uecc_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, - num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ - - mg_uecc_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ - mg_uecc_vli_modSub(t7, t7, t6, curve->p, - num_words); /* t7 = F - (B + C) = x3' */ - mg_uecc_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ - mg_uecc_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ - mg_uecc_vli_modSub(Y1, t6, Y1, curve->p, - num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ - - mg_uecc_vli_set(X1, t7, num_words); -} - -/* result may overlap point. */ -static void EccPoint_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, - const mg_uecc_word_t *initial_Z, bitcount_t num_bits, - MG_UECC_Curve curve) { - /* R0 and R1 */ - mg_uecc_word_t Rx[2][MG_UECC_MAX_WORDS]; - mg_uecc_word_t Ry[2][MG_UECC_MAX_WORDS]; - mg_uecc_word_t z[MG_UECC_MAX_WORDS]; - bitcount_t i; - mg_uecc_word_t nb; - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_set(Rx[1], point, num_words); - mg_uecc_vli_set(Ry[1], point + num_words, num_words); - - XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); - - for (i = num_bits - 2; i > 0; --i) { - nb = !mg_uecc_vli_testBit(scalar, i); - XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); - XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); - } - - nb = !mg_uecc_vli_testBit(scalar, 0); - XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); - - /* Find final 1/Z value. */ - mg_uecc_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ - mg_uecc_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ - mg_uecc_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ - mg_uecc_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ - /* yP / (xP * Yb * (X1 - X0)) */ - mg_uecc_vli_modMult_fast(z, z, point + num_words, curve); - mg_uecc_vli_modMult_fast(z, z, Rx[1 - nb], - curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ - /* End 1/Z calculation */ - - XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); - apply_z(Rx[0], Ry[0], z, curve); - - mg_uecc_vli_set(result, Rx[0], num_words); - mg_uecc_vli_set(result + num_words, Ry[0], num_words); -} - -static mg_uecc_word_t regularize_k(const mg_uecc_word_t *const k, - mg_uecc_word_t *k0, mg_uecc_word_t *k1, - MG_UECC_Curve curve) { - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - mg_uecc_word_t carry = - mg_uecc_vli_add(k0, k, curve->n, num_n_words) || - (num_n_bits < ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8) && - mg_uecc_vli_testBit(k0, num_n_bits)); - mg_uecc_vli_add(k1, k0, curve->n, num_n_words); - return carry; -} - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -MG_UECC_VLI_API int mg_uecc_generate_random_int(mg_uecc_word_t *random, - const mg_uecc_word_t *top, - wordcount_t num_words) { - mg_uecc_word_t mask = (mg_uecc_word_t) -1; - mg_uecc_word_t tries; - bitcount_t num_bits = mg_uecc_vli_numBits(top, num_words); - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - unsigned int len = (unsigned int) (num_words * MG_UECC_WORD_SIZE); - if (!(g_rng_function == NULL ? mg_random((uint8_t *) random, len) - : g_rng_function((uint8_t *) random, len))) - return 0; - random[num_words - 1] &= - mask >> ((bitcount_t) (num_words * MG_UECC_WORD_SIZE * 8 - num_bits)); - if (!mg_uecc_vli_isZero(random, num_words) && - mg_uecc_vli_cmp(top, random, num_words) == 1) { - return 1; - } - } - return 0; -} - -static mg_uecc_word_t EccPoint_compute_public_key(mg_uecc_word_t *result, - mg_uecc_word_t *private_key, - MG_UECC_Curve curve) { - mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *p2[2] = {tmp1, tmp2}; - mg_uecc_word_t *initial_Z = 0; - mg_uecc_word_t carry; - - /* Regularize the bitcount for the private key so that attackers cannot use a - side channel attack to learn the number of leading zeros. */ - carry = regularize_k(private_key, tmp1, tmp2, curve); - - /* If an RNG function was specified, try to get a random initial Z value to - improve protection against side-channel attacks. */ - if (g_rng_function) { - if (!mg_uecc_generate_random_int(p2[carry], curve->p, curve->num_words)) { - return 0; - } - initial_Z = p2[carry]; - } - EccPoint_mult(result, curve->G, p2[!carry], initial_Z, - (bitcount_t) (curve->num_n_bits + 1), curve); - - if (EccPoint_isZero(result, curve)) { - return 0; - } - return 1; -} - -#if MG_UECC_WORD_SIZE == 1 - -MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const uint8_t *native) { - wordcount_t i; - for (i = 0; i < num_bytes; ++i) { - bytes[i] = native[(num_bytes - 1) - i]; - } -} - -MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(uint8_t *native, - const uint8_t *bytes, - int num_bytes) { - mg_uecc_vli_nativeToBytes(native, num_bytes, bytes); -} - -#else - -MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const mg_uecc_word_t *native) { - int i; - for (i = 0; i < num_bytes; ++i) { - unsigned b = (unsigned) (num_bytes - 1 - i); - bytes[i] = (uint8_t) (native[b / MG_UECC_WORD_SIZE] >> - (8 * (b % MG_UECC_WORD_SIZE))); - } -} - -MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, - const uint8_t *bytes, - int num_bytes) { - int i; - mg_uecc_vli_clear(native, - (wordcount_t) ((num_bytes + (MG_UECC_WORD_SIZE - 1)) / - MG_UECC_WORD_SIZE)); - for (i = 0; i < num_bytes; ++i) { - unsigned b = (unsigned) (num_bytes - 1 - i); - native[b / MG_UECC_WORD_SIZE] |= (mg_uecc_word_t) bytes[i] - << (8 * (b % MG_UECC_WORD_SIZE)); - } -} - -#endif /* MG_UECC_WORD_SIZE */ - -int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, - MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key; - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _private[MG_UECC_MAX_WORDS]; - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t tries; - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - if (!mg_uecc_generate_random_int(_private, curve->n, - BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (EccPoint_compute_public_key(_public, _private, curve)) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), - _private); - mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public); - mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, - _public + curve->num_words); -#endif - return 1; - } - } - return 0; -} - -int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, - uint8_t *secret, MG_UECC_Curve curve) { - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; - mg_uecc_word_t _private[MG_UECC_MAX_WORDS]; - - mg_uecc_word_t tmp[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *p2[2] = {_private, tmp}; - mg_uecc_word_t *initial_Z = 0; - mg_uecc_word_t carry; - wordcount_t num_words = curve->num_words; - wordcount_t num_bytes = curve->num_bytes; - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) _private, private_key, num_bytes); - bcopy((uint8_t *) _public, public_key, num_bytes * 2); -#else - mg_uecc_vli_bytesToNative(_private, private_key, - BITS_TO_BYTES(curve->num_n_bits)); - mg_uecc_vli_bytesToNative(_public, public_key, num_bytes); - mg_uecc_vli_bytesToNative(_public + num_words, public_key + num_bytes, - num_bytes); -#endif - - /* Regularize the bitcount for the private key so that attackers cannot use a - side channel attack to learn the number of leading zeros. */ - carry = regularize_k(_private, _private, tmp, curve); - - /* If an RNG function was specified, try to get a random initial Z value to - improve protection against side-channel attacks. */ - if (g_rng_function) { - if (!mg_uecc_generate_random_int(p2[carry], curve->p, num_words)) { - return 0; - } - initial_Z = p2[carry]; - } - - EccPoint_mult(_public, _public, p2[!carry], initial_Z, - (bitcount_t) (curve->num_n_bits + 1), curve); -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes); -#else - mg_uecc_vli_nativeToBytes(secret, num_bytes, _public); -#endif - return !EccPoint_isZero(_public, curve); -} - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, - MG_UECC_Curve curve) { - wordcount_t i; - for (i = 0; i < curve->num_bytes; ++i) { - compressed[i + 1] = public_key[i]; - } -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01); -#else - compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); -#endif -} - -void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, - MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *point = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t point[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t *y = point + curve->num_words; -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy(public_key, compressed + 1, curve->num_bytes); -#else - mg_uecc_vli_bytesToNative(point, compressed + 1, curve->num_bytes); -#endif - curve->x_side(y, point, curve); - curve->mod_sqrt(y, curve); - - if ((uint8_t) (y[0] & 0x01) != (compressed[0] & 0x01)) { - mg_uecc_vli_sub(y, curve->p, y, curve->num_words); - } - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, point); - mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); -#endif -} -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -MG_UECC_VLI_API int mg_uecc_valid_point(const mg_uecc_word_t *point, - MG_UECC_Curve curve) { - mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - /* The point at infinity is invalid. */ - if (EccPoint_isZero(point, curve)) { - return 0; - } - - /* x and y must be smaller than p. */ - if (mg_uecc_vli_cmp_unsafe(curve->p, point, num_words) != 1 || - mg_uecc_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { - return 0; - } - - mg_uecc_vli_modSquare_fast(tmp1, point + num_words, curve); - curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ - - /* Make sure that y^2 == x^3 + ax + b */ - return (int) (mg_uecc_vli_equal(tmp1, tmp2, num_words)); -} - -int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes); - mg_uecc_vli_bytesToNative(_public + curve->num_words, - public_key + curve->num_bytes, curve->num_bytes); -#endif - return mg_uecc_valid_point(_public, curve); -} - -int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, - MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key; - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _private[MG_UECC_MAX_WORDS]; - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_bytesToNative(_private, private_key, - BITS_TO_BYTES(curve->num_n_bits)); -#endif - - /* Make sure the private key is in the range [1, n-1]. */ - if (mg_uecc_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (mg_uecc_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != - 1) { - return 0; - } - - /* Compute public key. */ - if (!EccPoint_compute_public_key(_public, _private, curve)) { - return 0; - } - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public); - mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, - _public + curve->num_words); -#endif - return 1; -} - -/* -------- ECDSA code -------- */ - -static void bits2int(mg_uecc_word_t *native, const uint8_t *bits, - unsigned bits_size, MG_UECC_Curve curve) { - unsigned num_n_bytes = (unsigned) BITS_TO_BYTES(curve->num_n_bits); - unsigned num_n_words = (unsigned) BITS_TO_WORDS(curve->num_n_bits); - int shift; - mg_uecc_word_t carry; - mg_uecc_word_t *ptr; - - if (bits_size > num_n_bytes) { - bits_size = num_n_bytes; - } - - mg_uecc_vli_clear(native, (wordcount_t) num_n_words); -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) native, bits, bits_size); -#else - mg_uecc_vli_bytesToNative(native, bits, (int) bits_size); -#endif - if (bits_size * 8 <= (unsigned) curve->num_n_bits) { - return; - } - shift = (int) bits_size * 8 - curve->num_n_bits; - carry = 0; - ptr = native + num_n_words; - while (ptr-- > native) { - mg_uecc_word_t temp = *ptr; - *ptr = (temp >> shift) | carry; - carry = temp << (MG_UECC_WORD_BITS - shift); - } - - /* Reduce mod curve_n */ - if (mg_uecc_vli_cmp_unsafe(curve->n, native, (wordcount_t) num_n_words) != - 1) { - mg_uecc_vli_sub(native, native, curve->n, (wordcount_t) num_n_words); - } -} - -static int mg_uecc_sign_with_k_internal(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, mg_uecc_word_t *k, - uint8_t *signature, - MG_UECC_Curve curve) { - mg_uecc_word_t tmp[MG_UECC_MAX_WORDS]; - mg_uecc_word_t s[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *k2[2] = {tmp, s}; - mg_uecc_word_t *initial_Z = 0; -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *p = (mg_uecc_word_t *) signature; -#else - mg_uecc_word_t p[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t carry; - wordcount_t num_words = curve->num_words; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - - /* Make sure 0 < k < curve_n */ - if (mg_uecc_vli_isZero(k, num_words) || - mg_uecc_vli_cmp(curve->n, k, num_n_words) != 1) { - return 0; - } - - carry = regularize_k(k, tmp, s, curve); - /* If an RNG function was specified, try to get a random initial Z value to - improve protection against side-channel attacks. */ - if (g_rng_function) { - if (!mg_uecc_generate_random_int(k2[carry], curve->p, num_words)) { - return 0; - } - initial_Z = k2[carry]; - } - EccPoint_mult(p, curve->G, k2[!carry], initial_Z, - (bitcount_t) (num_n_bits + 1), curve); - if (mg_uecc_vli_isZero(p, num_words)) { - return 0; - } - - /* If an RNG function was specified, get a random number - to prevent side channel analysis of k. */ - if (!g_rng_function) { - mg_uecc_vli_clear(tmp, num_n_words); - tmp[0] = 1; - } else if (!mg_uecc_generate_random_int(tmp, curve->n, num_n_words)) { - return 0; - } - - /* Prevent side channel analysis of mg_uecc_vli_modInv() to determine - bits of k / the private key by premultiplying by a random number */ - mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ - mg_uecc_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ - mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ -#endif - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); -#else - mg_uecc_vli_bytesToNative(tmp, private_key, - BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ -#endif - - s[num_n_words - 1] = 0; - mg_uecc_vli_set(s, p, num_words); - mg_uecc_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ - - bits2int(tmp, message_hash, hash_size, curve); - mg_uecc_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ - mg_uecc_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ - if (mg_uecc_vli_numBits(s, num_n_words) > (bitcount_t) curve->num_bytes * 8) { - return 0; - } -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s, - curve->num_bytes); -#else - mg_uecc_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); -#endif - return 1; -} - -#if 0 -/* For testing - sign with an explicitly specified k value */ -int mg_uecc_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *k, uint8_t *signature, - MG_UECC_Curve curve) { - mg_uecc_word_t k2[MG_UECC_MAX_WORDS]; - bits2int(k2, k, (unsigned) BITS_TO_BYTES(curve->num_n_bits), curve); - return mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k2, - signature, curve); -} -#endif - -int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve) { - mg_uecc_word_t k[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tries; - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - if (!mg_uecc_generate_random_int(k, curve->n, - BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k, - signature, curve)) { - return 1; - } - } - return 0; -} - -/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always - the same size as the hash result size. */ -static void HMAC_init(const MG_UECC_HashContext *hash_context, - const uint8_t *K) { - uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x36; - for (; i < hash_context->block_size; ++i) pad[i] = 0x36; - - hash_context->init_hash(hash_context); - hash_context->update_hash(hash_context, pad, hash_context->block_size); -} - -static void HMAC_update(const MG_UECC_HashContext *hash_context, - const uint8_t *message, unsigned message_size) { - hash_context->update_hash(hash_context, message, message_size); -} - -static void HMAC_finish(const MG_UECC_HashContext *hash_context, - const uint8_t *K, uint8_t *result) { - uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x5c; - for (; i < hash_context->block_size; ++i) pad[i] = 0x5c; - - hash_context->finish_hash(hash_context, result); - - hash_context->init_hash(hash_context); - hash_context->update_hash(hash_context, pad, hash_context->block_size); - hash_context->update_hash(hash_context, result, hash_context->result_size); - hash_context->finish_hash(hash_context, result); -} - -/* V = HMAC_K(V) */ -static void update_V(const MG_UECC_HashContext *hash_context, uint8_t *K, - uint8_t *V) { - HMAC_init(hash_context, K); - HMAC_update(hash_context, V, hash_context->result_size); - HMAC_finish(hash_context, K, V); -} - -/* Deterministic signing, similar to RFC 6979. Differences are: - * We just use H(m) directly rather than bits2octets(H(m)) - (it is not reduced modulo curve_n). - * We generate a value for k (aka T) directly rather than converting - endianness. - - Layout of hash_context->tmp: | | (1 byte overlapped 0x00 or 0x01) / - */ -int mg_uecc_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, unsigned hash_size, - const MG_UECC_HashContext *hash_context, - uint8_t *signature, MG_UECC_Curve curve) { - uint8_t *K = hash_context->tmp; - uint8_t *V = K + hash_context->result_size; - wordcount_t num_bytes = curve->num_bytes; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - mg_uecc_word_t tries; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) { - V[i] = 0x01; - K[i] = 0; - } - - /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x00; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_update(hash_context, private_key, (unsigned int) num_bytes); - HMAC_update(hash_context, message_hash, hash_size); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - - /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x01; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_update(hash_context, private_key, (unsigned int) num_bytes); - HMAC_update(hash_context, message_hash, hash_size); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - mg_uecc_word_t T[MG_UECC_MAX_WORDS]; - uint8_t *T_ptr = (uint8_t *) T; - wordcount_t T_bytes = 0; - for (;;) { - update_V(hash_context, K, V); - for (i = 0; i < hash_context->result_size; ++i) { - T_ptr[T_bytes++] = V[i]; - if (T_bytes >= num_n_words * MG_UECC_WORD_SIZE) { - goto filled; - } - } - } - filled: - if ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8 > num_n_bits) { - mg_uecc_word_t mask = (mg_uecc_word_t) -1; - T[num_n_words - 1] &= - mask >> - ((bitcount_t) (num_n_words * MG_UECC_WORD_SIZE * 8 - num_n_bits)); - } - - if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, T, - signature, curve)) { - return 1; - } - - /* K = HMAC_K(V || 0x00) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x00; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - } - return 0; -} - -static bitcount_t smax(bitcount_t a, bitcount_t b) { - return (a > b ? a : b); -} - -int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *signature, - MG_UECC_Curve curve) { - mg_uecc_word_t u1[MG_UECC_MAX_WORDS], u2[MG_UECC_MAX_WORDS]; - mg_uecc_word_t z[MG_UECC_MAX_WORDS]; - mg_uecc_word_t sum[MG_UECC_MAX_WORDS * 2]; - mg_uecc_word_t rx[MG_UECC_MAX_WORDS]; - mg_uecc_word_t ry[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tx[MG_UECC_MAX_WORDS]; - mg_uecc_word_t ty[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tz[MG_UECC_MAX_WORDS]; - const mg_uecc_word_t *points[4]; - const mg_uecc_word_t *point; - bitcount_t num_bits; - bitcount_t i; -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t r[MG_UECC_MAX_WORDS], s[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - - rx[num_n_words - 1] = 0; - r[num_n_words - 1] = 0; - s[num_n_words - 1] = 0; - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) r, signature, curve->num_bytes); - bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes); -#else - mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes); - mg_uecc_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes, - curve->num_bytes); - mg_uecc_vli_bytesToNative(r, signature, curve->num_bytes); - mg_uecc_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); -#endif - - /* r, s must not be 0. */ - if (mg_uecc_vli_isZero(r, num_words) || mg_uecc_vli_isZero(s, num_words)) { - return 0; - } - - /* r, s must be < n. */ - if (mg_uecc_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || - mg_uecc_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { - return 0; - } - - /* Calculate u1 and u2. */ - mg_uecc_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ - u1[num_n_words - 1] = 0; - bits2int(u1, message_hash, hash_size, curve); - mg_uecc_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ - mg_uecc_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ - - /* Calculate sum = G + Q. */ - mg_uecc_vli_set(sum, _public, num_words); - mg_uecc_vli_set(sum + num_words, _public + num_words, num_words); - mg_uecc_vli_set(tx, curve->G, num_words); - mg_uecc_vli_set(ty, curve->G + num_words, num_words); - mg_uecc_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ - XYcZ_add(tx, ty, sum, sum + num_words, curve); - mg_uecc_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ - apply_z(sum, sum + num_words, z, curve); - - /* Use Shamir's trick to calculate u1*G + u2*Q */ - points[0] = 0; - points[1] = curve->G; - points[2] = _public; - points[3] = sum; - num_bits = smax(mg_uecc_vli_numBits(u1, num_n_words), - mg_uecc_vli_numBits(u2, num_n_words)); - point = - points[(!!mg_uecc_vli_testBit(u1, (bitcount_t) (num_bits - 1))) | - ((!!mg_uecc_vli_testBit(u2, (bitcount_t) (num_bits - 1))) << 1)]; - mg_uecc_vli_set(rx, point, num_words); - mg_uecc_vli_set(ry, point + num_words, num_words); - mg_uecc_vli_clear(z, num_words); - z[0] = 1; - - for (i = num_bits - 2; i >= 0; --i) { - mg_uecc_word_t index; - curve->double_jacobian(rx, ry, z, curve); - - index = (!!mg_uecc_vli_testBit(u1, i)) | - (mg_uecc_word_t) ((!!mg_uecc_vli_testBit(u2, i)) << 1); - point = points[index]; - if (point) { - mg_uecc_vli_set(tx, point, num_words); - mg_uecc_vli_set(ty, point + num_words, num_words); - apply_z(tx, ty, z, curve); - mg_uecc_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ - XYcZ_add(tx, ty, rx, ry, curve); - mg_uecc_vli_modMult_fast(z, z, tz, curve); - } - } - - mg_uecc_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ - apply_z(rx, ry, z, curve); - - /* v = x1 (mod n) */ - if (mg_uecc_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { - mg_uecc_vli_sub(rx, rx, curve->n, num_n_words); - } - - /* Accept only if v == r. */ - return (int) (mg_uecc_vli_equal(rx, r, num_words)); -} - -#if MG_UECC_ENABLE_VLI_API - -unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve) { - return curve->num_words; -} - -unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve) { - return curve->num_bytes; -} - -unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve) { - return curve->num_bytes * 8; -} - -unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve) { - return BITS_TO_WORDS(curve->num_n_bits); -} - -unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve) { - return BITS_TO_BYTES(curve->num_n_bits); -} - -unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve) { - return curve->num_n_bits; -} - -const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve) { - return curve->p; -} - -const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve) { - return curve->n; -} - -const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve) { - return curve->G; -} - -const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve) { - return curve->b; -} - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve) { - curve->mod_sqrt(a, curve); -} -#endif - -void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, - MG_UECC_Curve curve) { -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - mg_uecc_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, MG_UECC_Curve curve) { - mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *p2[2] = {tmp1, tmp2}; - mg_uecc_word_t carry = regularize_k(scalar, tmp1, tmp2, curve); - - EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve); -} - -#endif /* MG_UECC_ENABLE_VLI_API */ -#endif // MG_TLS_BUILTIN -// End of uecc BSD-2 - -#ifdef MG_ENABLE_LINES -#line 1 "src/tls_x25519.c" -#endif -/** - * Adapted from STROBE: https://strobe.sourceforge.io/ - * Copyright (c) 2015-2016 Cryptography Research, Inc. - * Author: Mike Hamburg - * License: MIT License - */ - - - - -#if MG_TLS == MG_TLS_BUILTIN - -const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9}; - -#define X25519_WBITS 32 - -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -typedef int64_t sdlimb_t; - -#define NLIMBS (256 / X25519_WBITS) -typedef limb_t mg_fe[NLIMBS]; - -static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) { - dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry; - *carry = (limb_t) (tmp >> X25519_WBITS); - return (limb_t) tmp; -} - -// These functions are implemented in terms of umaal on ARM -static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) { - dlimb_t total = (dlimb_t) *carry + acc + mand; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -static limb_t adc0(limb_t *carry, limb_t acc) { - dlimb_t total = (dlimb_t) *carry + acc; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -// - Precondition: carry is small. -// - Invariant: result of propagate is < 2^255 + 1 word -// - In particular, always less than 2p. -// - Also, output x >= min(x,19) -static void propagate(mg_fe x, limb_t over) { - unsigned i; - limb_t carry; - over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1; - x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1)); - - carry = over * 19; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry, x[i]); - } -} - -static void add(mg_fe out, const mg_fe a, const mg_fe b) { - unsigned i; - limb_t carry = 0; - for (i = 0; i < NLIMBS; i++) { - out[i] = adc(&carry, a[i], b[i]); - } - propagate(out, carry); -} - -static void sub(mg_fe out, const mg_fe a, const mg_fe b) { - unsigned i; - sdlimb_t carry = -38; - for (i = 0; i < NLIMBS; i++) { - carry = carry + a[i] - b[i]; - out[i] = (limb_t) carry; - carry >>= X25519_WBITS; - } - propagate(out, (limb_t) (1 + carry)); -} - -// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `mg_fe` -// to avoid build warnings -static void mul(mg_fe out, const mg_fe a, const limb_t *b, unsigned nb) { - limb_t accum[2 * NLIMBS] = {0}; - unsigned i, j; - - limb_t carry2; - for (i = 0; i < nb; i++) { - limb_t mand = b[i]; - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - limb_t tmp; // "a" may be misaligned - memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy - accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp); - } - accum[i + j] = carry2; - } - - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]); - } - propagate(out, carry2); -} - -static void sqr(mg_fe out, const mg_fe a) { - mul(out, a, a, NLIMBS); -} -static void mul1(mg_fe out, const mg_fe a) { - mul(out, a, out, NLIMBS); -} -static void sqr1(mg_fe a) { - mul1(a, a); -} - -static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS], - limb_t doswap) { - unsigned i; - for (i = 0; i < 2 * NLIMBS; i++) { - limb_t xor_ab = (a[i] ^ b[i]) & doswap; - a[i] ^= xor_ab; - b[i] ^= xor_ab; - } -} - -// Canonicalize a field element x, reducing it to the least residue which is -// congruent to it mod 2^255-19 -// - Precondition: x < 2^255 + 1 word -static limb_t canon(mg_fe x) { - // First, add 19. - unsigned i; - limb_t carry0 = 19; - limb_t res; - sdlimb_t carry; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry0, x[i]); - } - propagate(x, carry0); - - // Here, 19 <= x2 < 2^255 - // - This is because we added 19, so before propagate it can't be less - // than 19. After propagate, it still can't be less than 19, because if - // propagate does anything it adds 19. - // - We know that the high bit must be clear, because either the input was ~ - // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or - // it was < 2^255. So now, if we subtract 19, we will get back to something in - // [0,2^255-19). - carry = -19; - res = 0; - for (i = 0; i < NLIMBS; i++) { - carry += x[i]; - res |= x[i] = (limb_t) carry; - carry >>= X25519_WBITS; - } - return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS); -} - -static const limb_t a24[1] = {121665}; - -static void ladder_part1(mg_fe xs[5]) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - add(t1, x2, z2); // t1 = A - sub(z2, x2, z2); // z2 = B - add(x2, x3, z3); // x2 = C - sub(z3, x3, z3); // z3 = D - mul1(z3, t1); // z3 = DA - mul1(x2, z2); // x3 = BC - add(x3, z3, x2); // x3 = DA+CB - sub(z3, z3, x2); // z3 = DA-CB - sqr1(t1); // t1 = AA - sqr1(z2); // z2 = BB - sub(x2, t1, z2); // x2 = E = AA-BB - mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24 - add(z2, z2, t1); // z2 = E*a24 + AA -} - -static void ladder_part2(mg_fe xs[5], const mg_fe x1) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - sqr1(z3); // z3 = (DA-CB)^2 - mul1(z3, x1); // z3 = x1 * (DA-CB)^2 - sqr1(x3); // x3 = (DA+CB)^2 - mul1(z2, x2); // z2 = AA*(E*a24+AA) - sub(x2, t1, x2); // x2 = BB again - mul1(x2, t1); // x2 = AA*BB -} - -static void x25519_core(mg_fe xs[5], const uint8_t scalar[X25519_BYTES], - const uint8_t *x1, int clamp) { - int i; - mg_fe x1_limbs; - limb_t swap = 0; - limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3]; - memset(xs, 0, 4 * sizeof(mg_fe)); - x2[0] = z3[0] = 1; - for (i = 0; i < NLIMBS; i++) { - x3[i] = x1_limbs[i] = - MG_U32(x1[i * 4 + 3], x1[i * 4 + 2], x1[i * 4 + 1], x1[i * 4]); - } - - for (i = 255; i >= 0; i--) { - uint8_t bytei = scalar[i / 8]; - limb_t doswap; - if (clamp) { - if (i / 8 == 0) { - bytei &= (uint8_t) ~7U; - } else if (i / 8 == X25519_BYTES - 1) { - bytei &= 0x7F; - bytei |= 0x40; - } - } - doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1); - condswap(x2, x3, swap ^ doswap); - swap = doswap; - - ladder_part1(xs); - ladder_part2(xs, (const limb_t *) x1_limbs); - } - condswap(x2, x3, swap); -} - -int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp) { - int i, ret; - mg_fe xs[5], out_limbs; - limb_t *x2, *z2, *z3, *prev; - static const struct { - uint8_t a, c, n; - } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1}, - {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125}, - {3, 1, 2}, {3, 1, 2}, {3, 1, 1}}; - x25519_core(xs, scalar, x1, clamp); - - // Precomputed inversion chain - x2 = xs[0]; - z2 = xs[1]; - z3 = xs[3]; - - prev = z2; - for (i = 0; i < 13; i++) { - int j; - limb_t *a = xs[steps[i].a]; - for (j = steps[i].n; j > 0; j--) { - sqr(a, prev); - prev = a; - } - mul1(a, xs[steps[i].c]); - } - - // Here prev = z3 - // x2 /= z2 - mul(out_limbs, x2, z3, NLIMBS); - ret = (int) canon(out_limbs); - if (!clamp) ret = 0; - for (i = 0; i < NLIMBS; i++) { - uint32_t n = out_limbs[i]; - out[i * 4] = (uint8_t) (n & 0xff); - out[i * 4 + 1] = (uint8_t) ((n >> 8) & 0xff); - out[i * 4 + 2] = (uint8_t) ((n >> 16) & 0xff); - out[i * 4 + 3] = (uint8_t) ((n >> 24) & 0xff); - } - return ret; -} - -#endif - #ifdef MG_ENABLE_LINES #line 1 "src/url.c" #endif diff --git a/mongoose.h b/mongoose.h index dfcc3296..13b1c4f0 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1486,10 +1486,28 @@ struct timeval { #define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0 #endif +#ifndef MG_TLS_RECORD_SIZE +#define MG_TLS_RECORD_SIZE 16384 // Max plaintext per sent TLS record, builtin +#endif + #ifndef MG_ENABLE_CHACHA20 #define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN #endif +// P-256 implementation, for MG_TLS_BUILTIN +#define MG_P256_LENNGREN 1 // Cortex-M4/M7/M33 asm, see tls_p256_cortex.c +#define MG_P256_BEAR 3 // Portable C, BearSSL, see tls_p256.c + +#ifndef MG_P256 +#if defined(__GNUC__) && defined(__ARM_ARCH_PROFILE) && \ + __ARM_ARCH_PROFILE == 'M' && defined(__ARM_FEATURE_DSP) && \ + !defined(__ARM_BIG_ENDIAN) +#define MG_P256 MG_P256_LENNGREN // Cortex-M with DSP: M4, M7, M33, M55, M85 +#else +#define MG_P256 MG_P256_BEAR +#endif +#endif + #ifndef MG_ENABLE_HEALTH #define MG_ENABLE_HEALTH 0 #endif @@ -2964,26 +2982,13 @@ void mg_tls_ctx_free(struct mg_mgr *); #if MG_TLS == MG_TLS_BUILTIN // Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE // KEY input. Accepts PEM or DER. Returns key size, or 0 on error. -size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len); +size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len); #endif // Low-level IO primitives used by TLS layer enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 }; long mg_io_send(struct mg_connection *c, const void *buf, size_t len); long mg_io_recv(struct mg_connection *c, void *buf, size_t len); -#ifndef TLS_X15519_H -#define TLS_X15519_H - - - -#define X25519_BYTES 32 -extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; - -int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp); - - -#endif /* TLS_X15519_H */ /****************************************************************************** * * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL @@ -3052,665 +3057,227 @@ int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, #endif /* TLS_AES128_H */ // End of aes128 PD +// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by +// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN) +// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA +// code is always compiled, it is also used by P-384, see tls_p384.c +// +// Copyright (c) 2016 Thomas Pornin +// Copyright (c) 2017 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the +// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not +// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is +// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and +// public key directly -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ -#ifndef _UECC_H_ -#define _UECC_H_ -/* Platform selection options. -If MG_UECC_PLATFORM is not defined, the code will try to guess it based on -compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */ -#define mg_uecc_arch_other 0 -#define mg_uecc_x86 1 -#define mg_uecc_x86_64 2 -#define mg_uecc_arm 3 -#define mg_uecc_arm_thumb 4 -#define mg_uecc_arm_thumb2 5 -#define mg_uecc_arm64 6 -#define mg_uecc_avr 7 -/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform -(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will -be automatically set based on your platform. */ -/* Optimization level; trade speed for code size. - Larger values produce code that is faster but larger. - Currently supported values are 0 - 4; 0 is unusably slow for most - applications. Optimization level 4 currently only has an effect ARM platforms - where more than one curve is enabled. */ -#ifndef MG_UECC_OPTIMIZATION_LEVEL -#define MG_UECC_OPTIMIZATION_LEVEL 2 -#endif +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) -/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a -specific function to be used for (scalar) squaring instead of the generic -multiplication function. This can make things faster somewhat faster, but -increases the code size. */ -#ifndef MG_UECC_SQUARE_FUNC -#define MG_UECC_SQUARE_FUNC 0 -#endif +#define MG_BR_EC_secp256r1 23 // Curve IDs, same as TLS named groups +#define MG_BR_EC_secp384r1 24 +#define MG_BR_MAX_EC_SIZE 384 // Largest supported curve, bits -/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will -switch to native little-endian format for *all* arrays passed in and out of the -public API. This includes public and private keys, shared secrets, signatures -and message hashes. Using this switch reduces the amount of call stack memory -used by uECC, since less intermediate translations are required. Note that this -will *only* work on native little-endian processors and it will treat the -uint8_t arrays passed into the public API as word arrays, therefore requiring -the provided byte arrays to be word aligned on architectures that do not support -unaligned accesses. IMPORTANT: Keys and signatures generated with -MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures -generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same -endianness. */ -#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN -#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0 -#endif +// From BearSSL inc/bearssl_ec.h: an EC implementation +struct mg_br_ec_impl { + uint32_t supported_curves; // Bit mask of curve IDs + const unsigned char *(*generator)(int curve, size_t *len); + const unsigned char *(*order)(int curve, size_t *len); + size_t (*xoff)(int curve, size_t *len); + uint32_t (*mul)(unsigned char *G, size_t Glen, const unsigned char *x, + size_t xlen, int curve); + size_t (*mulgen)(unsigned char *R, const unsigned char *x, size_t xlen, + int curve); + uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve); +}; -/* Curve support selection. Set to 0 to remove that curve. */ -#ifndef MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_SUPPORTS_secp160r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp192r1 -#define MG_UECC_SUPPORTS_secp192r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp224r1 -#define MG_UECC_SUPPORTS_secp224r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp256r1 -#define MG_UECC_SUPPORTS_secp256r1 1 -#endif -#ifndef MG_UECC_SUPPORTS_secp256k1 -#define MG_UECC_SUPPORTS_secp256k1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp384r1 -#define MG_UECC_SUPPORTS_secp384r1 1 -#endif - -/* Specifies whether compressed point format is supported. - Set to 0 to disable point compression/decompression functions. */ -#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT -#define MG_UECC_SUPPORT_COMPRESSED_POINT 1 -#endif - -struct MG_UECC_Curve_t; -typedef const struct MG_UECC_Curve_t *MG_UECC_Curve; - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_SUPPORTS_secp160r1 -MG_UECC_Curve mg_uecc_secp160r1(void); -#endif -#if MG_UECC_SUPPORTS_secp192r1 -MG_UECC_Curve mg_uecc_secp192r1(void); -#endif -#if MG_UECC_SUPPORTS_secp224r1 -MG_UECC_Curve mg_uecc_secp224r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256r1 -MG_UECC_Curve mg_uecc_secp256r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256k1 -MG_UECC_Curve mg_uecc_secp256k1(void); -#endif -#if MG_UECC_SUPPORTS_secp384r1 -MG_UECC_Curve mg_uecc_secp384r1(void); -#endif - -/* MG_UECC_RNG_Function type -The RNG function should fill 'size' random bytes into 'dest'. It should return 1 -if 'dest' was filled with random data, or 0 if the random data could not be -generated. The filled-in values should be either truly random, or from a -cryptographically-secure PRNG. - -A correctly functioning RNG is required before calling mg_uecc_make_key() or -mg_uecc_sign(). - -Setting a correctly functioning RNG function improves the resistance to -side-channel attacks for mg_uecc_shared_secret() and -mg_uecc_sign_deterministic(). -*/ -typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size); - -// Mongoose: mg_uecc_make_key() and mg_uecc_sign() use mg_random() when no -// explicit uECC RNG is installed. If mg_random() cannot provide a strong source, -// those operations fail. Deterministic signing works without RNG, but only an -// explicit uECC RNG enables its optional side-channel blinding. - -/* mg_uecc_set_rng() function. -Set the function that will be used to generate random bytes. The RNG function -should return 1 if the random data was generated, or 0 if the random data could -not be generated. - -Mongoose: this overrides the default mg_random() fallback used by -mg_uecc_make_key() and mg_uecc_sign(). Do not call mg_uecc_set_rng() unless the -application must provide its own cryptographic RNG. - -Inputs: - rng_function - The function that will be used to generate random bytes. -*/ -void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function); - -/* mg_uecc_get_rng() function. - -Returns the function that will be used to generate random bytes. -*/ -MG_UECC_RNG_Function mg_uecc_get_rng(void); - -/* mg_uecc_curve_private_key_size() function. - -Returns the size of a private key for the curve in bytes. -*/ -int mg_uecc_curve_private_key_size(MG_UECC_Curve curve); - -/* mg_uecc_curve_public_key_size() function. - -Returns the size of a public key for the curve in bytes. -*/ -int mg_uecc_curve_public_key_size(MG_UECC_Curve curve); - -/* mg_uecc_make_key() function. -Create a public/private key pair. - -Outputs: - public_key - Will be filled in with the public key. Must be at least 2 * -the curve size (in bytes) long. For example, if the curve is secp256r1, -public_key must be 64 bytes long. private_key - Will be filled in with the -private key. Must be as long as the curve order; this is typically the same as -the curve size, except for secp160r1. For example, if the curve is secp256r1, -private_key must be 32 bytes long. - - For secp160r1, private_key must be 21 bytes long! Note that -the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it -being non-zero). - -Returns 1 if the key pair was generated successfully, 0 if an error occurred. -*/ -int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, - MG_UECC_Curve curve); - -/* mg_uecc_shared_secret() function. -Compute a shared secret given your secret key and someone else's public key. If -the public key is not from a trusted source and has not been previously -verified, you should verify it first using mg_uecc_valid_public_key(). Note: It -is recommended that you hash the result of mg_uecc_shared_secret() before using -it for symmetric encryption or HMAC. - -Inputs: - public_key - The public key of the remote party. - private_key - Your private key. - -Outputs: - secret - Will be filled in with the shared secret value. Must be the same -size as the curve size; for example, if the curve is secp256r1, secret must be -32 bytes long. - -Returns 1 if the shared secret was generated successfully, 0 if an error -occurred. -*/ -int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, - uint8_t *secret, MG_UECC_Curve curve); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* mg_uecc_compress() function. -Compress a public key. - -Inputs: - public_key - The public key to compress. - -Outputs: - compressed - Will be filled in with the compressed public key. Must be at -least (curve size + 1) bytes long; for example, if the curve is secp256r1, - compressed must be 33 bytes long. -*/ -void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, - MG_UECC_Curve curve); - -/* mg_uecc_decompress() function. -Decompress a compressed public key. - -Inputs: - compressed - The compressed public key. - -Outputs: - public_key - Will be filled in with the decompressed public key. -*/ -void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, - MG_UECC_Curve curve); -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -/* mg_uecc_valid_public_key() function. -Check to see if a public key is valid. - -Note that you are not required to check for a valid public key before using any -other uECC functions. However, you may wish to avoid spending CPU time computing -a shared secret or verifying a signature using an invalid public key. - -Inputs: - public_key - The public key to check. - -Returns 1 if the public key is valid, 0 if it is invalid. -*/ -int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve); - -/* mg_uecc_compute_public_key() function. -Compute the corresponding public key for a private key. - -Inputs: - private_key - The private key to compute the public key for - -Outputs: - public_key - Will be filled in with the corresponding public key - -Returns 1 if the key was computed successfully, 0 if an error occurred. -*/ -int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, - MG_UECC_Curve curve); - -/* mg_uecc_sign() function. -Generate an ECDSA signature for a given hash value. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it in to this function along with your private key. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - -Outputs: - signature - Will be filled in with the signature value. Must be at least 2 * -curve size long. For example, if the curve is secp256r1, signature must be 64 -bytes long. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve); - -/* MG_UECC_HashContext structure. -This is used to pass in an arbitrary hash function to -mg_uecc_sign_deterministic(). The structure will be used for multiple hash -computations; each time a new hash is computed, init_hash() will be called, -followed by one or more calls to update_hash(), and finally a call to -finish_hash() to produce the resulting hash. - -The intention is that you will create a structure that includes -MG_UECC_HashContext followed by any hash-specific data. For example: - -typedef struct SHA256_HashContext { - MG_UECC_HashContext uECC; - SHA256_CTX ctx; -} SHA256_HashContext; - -void init_SHA256(MG_UECC_HashContext *base) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Init(&context->ctx); +// From BearSSL src/inner.h: constant-time helpers +static inline uint32_t MG_BR_NOT(uint32_t ctl) { + return ctl ^ 1; +} +static inline uint32_t MG_BR_MUX(uint32_t ctl, uint32_t x, uint32_t y) { + return y ^ (-ctl & (x ^ y)); +} +static inline uint32_t MG_BR_EQ(uint32_t x, uint32_t y) { + uint32_t q = x ^ y; + return MG_BR_NOT((q | -q) >> 31); +} +static inline uint32_t MG_BR_NEQ(uint32_t x, uint32_t y) { + uint32_t q = x ^ y; + return (q | -q) >> 31; +} +static inline uint32_t MG_BR_GT(uint32_t x, uint32_t y) { + uint32_t z = y - x; + return (z ^ ((x ^ y) & (x ^ z))) >> 31; +} +#define MG_BR_GE(x, y) MG_BR_NOT(MG_BR_GT(y, x)) +#define MG_BR_LT(x, y) MG_BR_GT(y, x) +static inline int32_t MG_BR_CMP(uint32_t x, uint32_t y) { + return (int32_t) MG_BR_GT(x, y) | -(int32_t) MG_BR_GT(y, x); +} +static inline uint32_t MG_BR_BIT_LENGTH(uint32_t x) { + uint32_t k, c; + k = MG_BR_NEQ(x, 0); + c = MG_BR_GT(x, 0xFFFF), x = MG_BR_MUX(c, x >> 16, x), k += c << 4; + c = MG_BR_GT(x, 0x00FF), x = MG_BR_MUX(c, x >> 8, x), k += c << 3; + c = MG_BR_GT(x, 0x000F), x = MG_BR_MUX(c, x >> 4, x), k += c << 2; + c = MG_BR_GT(x, 0x0003), x = MG_BR_MUX(c, x >> 2, x), k += c << 1; + k += MG_BR_GT(x, 0x0001); + return k; +} +#define MG_BR_MUL31(x, y) ((uint64_t) (x) * (uint64_t) (y)) +#define MG_BR_MUL31_lo(x, y) \ + (((uint32_t) (x) * (uint32_t) (y)) & (uint32_t) 0x7FFFFFFF) +#define MG_BR_CCOPY mg_br_ccopy +void mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len); +uint32_t mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r); +static inline uint32_t mg_br_rem(uint32_t hi, uint32_t lo, uint32_t d) { + uint32_t r; + mg_br_divrem(hi, lo, d, &r); + return r; +} +static inline uint32_t mg_br_div(uint32_t hi, uint32_t lo, uint32_t d) { + uint32_t r; + return mg_br_divrem(hi, lo, d, &r); +} +static inline void mg_br_enc32be(void *dst, uint32_t x) { + unsigned char *buf = (unsigned char *) dst; + buf[0] = (unsigned char) (x >> 24), buf[1] = (unsigned char) (x >> 16); + buf[2] = (unsigned char) (x >> 8), buf[3] = (unsigned char) x; +} +static inline void mg_br_i31_zero(uint32_t *x, uint32_t bit_len) { + *x++ = bit_len; + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x); } -void update_SHA256(MG_UECC_HashContext *base, - const uint8_t *message, - unsigned message_size) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Update(&context->ctx, message, message_size); -} - -void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Final(hash_result, &context->ctx); -} - -... when signing ... -{ - uint8_t tmp[32 + 32 + 64]; - SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, -32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature); -} -*/ -typedef struct MG_UECC_HashContext { - void (*init_hash)(const struct MG_UECC_HashContext *context); - void (*update_hash)(const struct MG_UECC_HashContext *context, - const uint8_t *message, unsigned message_size); - void (*finish_hash)(const struct MG_UECC_HashContext *context, - uint8_t *hash_result); - unsigned - block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned - result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + - block_size) bytes. */ -} MG_UECC_HashContext; - -/* mg_uecc_sign_deterministic() function. -Generate an ECDSA signature for a given hash value, using a deterministic -algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng() -before calling this function; however, if the RNG is defined it will improve -resistance to side-channel attacks. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it to this function along with your private key and a hash context. Note -that the message_hash does not need to be computed with the same hash function -used by hash_context. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - hash_context - A hash context to use. - -Outputs: - signature - Will be filled in with the signature value. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, unsigned hash_size, - const MG_UECC_HashContext *hash_context, - uint8_t *signature, MG_UECC_Curve curve); - -/* mg_uecc_verify() function. -Verify an ECDSA signature. - -Usage: Compute the hash of the signed data using the same hash as the signer and -pass it to this function along with the signer's public key and the signature -values (r and s). - -Inputs: - public_key - The signer's public key. - message_hash - The hash of the signed data. - hash_size - The size of message_hash in bytes. - signature - The signature value. - -Returns 1 if the signature is valid, 0 if it is invalid. -*/ -int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *signature, - MG_UECC_Curve curve); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_TYPES_H_ -#define _UECC_TYPES_H_ - -#ifndef MG_UECC_PLATFORM -#if defined(__AVR__) && __AVR__ -#define MG_UECC_PLATFORM mg_uecc_avr -#elif defined(__thumb2__) || \ - defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ -#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 -#elif defined(__thumb__) -#define MG_UECC_PLATFORM mg_uecc_arm_thumb -#elif defined(__arm__) || defined(_M_ARM) -#define MG_UECC_PLATFORM mg_uecc_arm -#elif defined(__aarch64__) -#define MG_UECC_PLATFORM mg_uecc_arm64 -#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ - defined(__I86__) -#define MG_UECC_PLATFORM mg_uecc_x86 -#elif defined(__amd64__) || defined(_M_X64) -#define MG_UECC_PLATFORM mg_uecc_x86_64 -#else -#define MG_UECC_PLATFORM mg_uecc_arch_other -#endif -#endif - -#ifndef MG_UECC_ARM_USE_UMAAL -#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) -#define MG_UECC_ARM_USE_UMAAL 1 -#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ - (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) -#define MG_UECC_ARM_USE_UMAAL 1 -#else -#define MG_UECC_ARM_USE_UMAAL 0 -#endif -#endif - -#ifndef MG_UECC_WORD_SIZE -#if MG_UECC_PLATFORM == mg_uecc_avr -#define MG_UECC_WORD_SIZE 1 -#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) -#define MG_UECC_WORD_SIZE 8 -#else -#define MG_UECC_WORD_SIZE 4 -#endif -#endif - -#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ - (MG_UECC_WORD_SIZE != 8) -#error "Unsupported value for MG_UECC_WORD_SIZE" -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) -#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 1 -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ - (MG_UECC_WORD_SIZE != 4)) -#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 4 -#endif - -typedef int8_t wordcount_t; -typedef int16_t bitcount_t; -typedef int8_t cmpresult_t; - -#if (MG_UECC_WORD_SIZE == 1) - -typedef uint8_t mg_uecc_word_t; -typedef uint16_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80 -#define MG_UECC_WORD_BITS 8 -#define MG_UECC_WORD_BITS_SHIFT 3 -#define MG_UECC_WORD_BITS_MASK 0x07 - -#elif (MG_UECC_WORD_SIZE == 4) - -typedef uint32_t mg_uecc_word_t; -typedef uint64_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80000000 -#define MG_UECC_WORD_BITS 32 -#define MG_UECC_WORD_BITS_SHIFT 5 -#define MG_UECC_WORD_BITS_MASK 0x01F - -#elif (MG_UECC_WORD_SIZE == 8) - -typedef uint64_t mg_uecc_word_t; - -#define HIGH_BIT_SET 0x8000000000000000U -#define MG_UECC_WORD_BITS 64 -#define MG_UECC_WORD_BITS_SHIFT 6 -#define MG_UECC_WORD_BITS_MASK 0x03F - -#endif /* MG_UECC_WORD_SIZE */ - -#endif /* _UECC_TYPES_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_VLI_H_ -#define _UECC_VLI_H_ - -// -// - -/* Functions for raw large-integer manipulation. These are only available - if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */ -#ifndef MG_UECC_ENABLE_VLI_API -#define MG_UECC_ENABLE_VLI_API 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_ENABLE_VLI_API - -void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, - wordcount_t num_words); - -/* Returns nonzero if bit 'bit' of vli is set. */ -mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit); - -/* Counts the number of bits required to represent vli. */ -bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, - const wordcount_t max_words); - -/* Sets dest = src. */ -void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise */ -mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns sign of left - right, in constant time. */ -cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes vli = vli >> 1. */ -void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Computes result = left + right, returning carry. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left - right, returning borrow. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left * right. Result must be 2 * num_words long. */ -void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes result = left^2. Result must be 2 * num_words long. */ -void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left, - wordcount_t num_words); - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = product % mod, where product is 2N words long. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Calculates result = product (mod curve->p), where product is up to - 2 * curve->num_words long. */ -void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, - MG_UECC_Curve curve); - -/* Computes result = (left * right) % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left * right) % curve->p. */ -void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, MG_UECC_Curve curve); - -/* Computes result = left^2 % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Computes result = left^2 % curve->p. */ -void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve); - -/* Computes result = (1 / input) % mod.*/ -void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input, - const mg_uecc_word_t *mod, wordcount_t num_words); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* Calculates a = sqrt(a) (mod curve->p) */ -void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif - -/* Converts an integer in uECC native format to big-endian bytes. */ -void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const mg_uecc_word_t *native); -/* Converts big-endian bytes to an integer in uECC native format. */ -void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes, - int num_bytes); - -unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve); - -const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve); - -int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve); - -/* Multiplies a point by a scalar. Points are represented by the X coordinate - followed by the Y coordinate in the same array, both coordinates are - curve->num_words long. Note that scalar must be curve->num_n_words long (NOT - curve->num_words). */ -void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, MG_UECC_Curve curve); - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -int mg_uecc_generate_random_int(mg_uecc_word_t *random, - const mg_uecc_word_t *top, - wordcount_t num_words); - -#endif /* MG_UECC_ENABLE_VLI_API */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_VLI_H_ */ - -// End of uecc BSD-2 +// Big integers, "i31" format, see BearSSL src/inner.h +uint32_t mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl); +uint32_t mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl); +uint32_t mg_br_i31_bit_length(uint32_t *x, size_t xlen); +uint32_t mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len, + const uint32_t *m); +void mg_br_i31_decode(uint32_t *x, const void *src, size_t len); +void mg_br_i31_encode(void *dst, size_t len, const uint32_t *x); +void mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i); +void mg_br_i31_to_monty(uint32_t *x, const uint32_t *m); +uint32_t mg_br_i31_iszero(const uint32_t *x); +void mg_br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, + uint32_t *t2); +void mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i); +void mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m); +uint32_t mg_br_i31_ninv31(uint32_t x); +void mg_br_i31_rshift(uint32_t *x, int count); + +// ECDSA, see BearSSL src/ec/ecdsa_i31_*.c. q is an uncompressed point +void mg_br_ecdsa_i31_bits2int(uint32_t *x, const void *src, size_t len, + uint32_t ebitlen); +uint32_t mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash, size_t hash_len, + const unsigned char *q, size_t qlen, + const void *sig, size_t sig_len); +size_t mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash, size_t hash_len, + const unsigned char *x, size_t xlen, + const unsigned char *k, void *sig); + +// P-256 (secp256r1) API. Keys and signatures are big endian byte strings, +// a public key is X || Y, a signature is r || s. Functions return 1 on +// success, 0 on failure + +// Generate a random key pair, using mg_random() +int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]); + +// Compute public key from a private key. Return 0 if private key is invalid +int mg_p256_compute_public_key(const uint8_t private_key[32], + uint8_t public_key[64]); + +// ECDH. Validates the peer public key, returns 0 if it is not on the curve +int mg_p256_shared_secret(const uint8_t public_key[64], + const uint8_t private_key[32], uint8_t secret[32]); + +// ECDSA sign with a deterministic k, RFC 6979 with HMAC-SHA256 +int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, uint8_t signature[64]); + +// ECDSA verify. Validates the public key +int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash, + size_t hash_size, const uint8_t signature[64]); + +// Internal: ECDSA sign with a given k, 32 bytes big endian, in [1, n-1]. +// Provided by the selected implementation, used by mg_p256_sign() +int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, const uint8_t k[32], + uint8_t signature[64]); +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) +// P-384 from BearSSL, https://bearssl.org commit 7bea48e +// P-384 signature verification, used by built-in TLS for certificates and +// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c +// +// Copyright (c) 2016 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static +// functions with p384_. Trimmed to P-384. Structs are not typedef-ed + + + + + + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) +// Verify ECDSA P-384 signature r || s over hash. Public key is X || Y. +// Return 1 on success, 0 on failure +int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash, + size_t hash_size, const uint8_t signature[96]); +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) // portable8439 v1.0.1 // Source: https://github.com/DavyLandman/portable8439 // Licensed under CC0-1.0 @@ -5425,9 +4992,6 @@ void mg_bsd_poll(struct mg_mgr *); #endif #if MG_ENABLE_BSD_PROTOTYPES -#if defined(FD_SET) || defined(FD_ZERO) || defined(FD_ISSET) -#error "Include mongoose.h before system select headers" -#endif typedef unsigned int socklen_t; typedef unsigned long nfds_t; typedef uint32_t in_addr_t; @@ -5471,6 +5035,8 @@ struct pollfd { int fd; short events; short revents; }; #ifndef FD_SETSIZE #define FD_SETSIZE 64 #endif + +#ifndef FD_ZERO #define _NFDBITS ((int) sizeof(__fd_mask) * 8) #define _howmany(x, y) (((x) + ((y) - 1)) / (y)) typedef struct fd_set { @@ -5487,6 +5053,7 @@ typedef struct fd_set { size_t _n = _howmany(FD_SETSIZE, _NFDBITS); \ while (_n > 0) _p->__fds_bits[--_n] = 0; \ } while (0) +#endif #define h_addr h_addr_list[0] #define AF_INET 2 diff --git a/resources/specs/areas/tls.md b/resources/specs/areas/tls.md index 3e71ad69..d2292b4b 100644 --- a/resources/specs/areas/tls.md +++ b/resources/specs/areas/tls.md @@ -13,9 +13,10 @@ Primary files: - `src/tls_dummy.c` - `src/tls_mbed.c` - `src/tls_openssl.c` +- `src/tls_p256_cortex.c` +- `src/tls_p256.c` +- `src/tls_p384.c` - `src/tls_rsa.c` -- `src/tls_uecc.c` -- `src/tls_x25519.c` Perform a focused security review centered on these files and this security surface. @@ -38,6 +39,6 @@ Trace attacker-controlled TLS peer input through normal client and server execut - Verify that wildcard matching is applied only to certificate presented identifiers, not to arbitrary reference identifiers or general DNS glob matching. - Review `mg_match` or other glob/matching helpers if used for certificate identity checks, authorization checks, filesystem paths, routing, or topic matching. Flag cases where glob semantics are broader than the security policy requires. - Check certificate parsing for DER/PEM boundary errors, length overflows, malformed ASN.1, extension parsing mistakes, name constraints, SAN vs CN precedence, embedded NUL, and invalid string type handling. -- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC/X25519 operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures. +- Review TLS record parsing, handshake transcript handling, key schedule, random generation, ECC (P-256, P-384) operations, AES-GCM nonce/tag handling, and error handling for memory safety and authentication failures. - Flag any behavior that silently falls back from verified TLS to unverified TLS or plaintext. - Check that TLS receive/send error paths cannot corrupt connection state, reuse freed buffers, or continue processing unauthenticated plaintext as encrypted data. diff --git a/sbom.cdx.json b/sbom.cdx.json index 59a6096f..73a1f97f 100644 --- a/sbom.cdx.json +++ b/sbom.cdx.json @@ -3,9 +3,9 @@ "bomFormat": "CycloneDX", "specVersion": "1.6", "serialNumber": "urn:uuid:d3f8b1e2-4a7c-4d9e-bf2a-1c3e5f7a9b0d", - "version": 1, + "version": 2, "metadata": { - "timestamp": "2026-08-12T00:00:00Z", + "timestamp": "2026-10-13T00:00:00Z", "component": { "type": "library", "bom-ref": "mongoose", @@ -34,14 +34,31 @@ "components": [ { "type": "library", - "bom-ref": "micro-ecc", - "author": "Kenneth MacKay", - "name": "micro-ecc", - "description": "ECC point operations on GF(p) curves (secp256r1). Used by Mongoose built-in TLS 1.3 for ECDH key exchange and ECDSA signatures. Source: src/tls_uecc.c, src/tls_uecc.h.", - "licenses": [ { "expression": "BSD-2-Clause" } ], - "purl": "pkg:github/kmackay/micro-ecc", + "bom-ref": "bearssl-ec", + "author": "Thomas Pornin", + "name": "BearSSL EC (P-256, P-384, ECDSA)", + "description": "P-256 (ec_p256_m31) and P-384 (ec_prime_i31) elliptic curve arithmetic, ECDSA sign and verify, and the i31 bignum code they use, derived from BearSSL. Used by Mongoose built-in TLS 1.3 for ECDHE key exchange and ECDSA signatures, by JWT ES256 and by OTA firmware signature verification. Source: src/tls_p256.c, src/tls_p256.h, src/tls_p384.c, src/tls_p384.h.", + "licenses": [ { "expression": "MIT" } ], "externalReferences": [ - { "type": "vcs", "url": "https://github.com/kmackay/micro-ecc" } + { "type": "website", "url": "https://bearssl.org" } + ], + "properties": [ + { "name": "upstream-commit", "value": "7bea48e5e850ab4cafbe68d3765cdaba13a86d6f" } + ] + }, + { + "type": "library", + "bom-ref": "p256-cortex-m4", + "author": "Emil Lenngren, Shortcut Labs AB", + "name": "P256-Cortex-M4", + "description": "P-256 ECDH and ECDSA in Cortex-M assembly. Used by Mongoose built-in TLS 1.3 instead of the BearSSL P-256 code on Cortex-M cores with the DSP extension (M4, M7, M33, M55, M85). Source: src/tls_p256_cortex.c.", + "licenses": [ { "expression": "MIT" } ], + "purl": "pkg:github/Emill/P256-Cortex-M4@ecd3ec2222fc9e18b6b44c86bb7183971a2041fc", + "externalReferences": [ + { "type": "vcs", "url": "https://github.com/Emill/P256-Cortex-M4" } + ], + "properties": [ + { "name": "upstream-commit", "value": "ecd3ec2222fc9e18b6b44c86bb7183971a2041fc" } ] }, { @@ -55,17 +72,6 @@ { "type": "website", "url": "https://bearssl.org" } ] }, - { - "type": "library", - "bom-ref": "strobe-x25519", - "author": "Cryptography Research, Inc. / Mike Hamburg", - "name": "STROBE x25519 (adapted)", - "description": "X25519 Diffie-Hellman function adapted from the STROBE project. Used by Mongoose built-in TLS 1.3 for ECDH key exchange. Source: src/tls_x25519.c.", - "licenses": [ { "expression": "MIT" } ], - "externalReferences": [ - { "type": "website", "url": "https://strobe.sourceforge.io/" } - ] - }, { "type": "library", "bom-ref": "sha1-reid", diff --git a/src/bsd.h b/src/bsd.h index e43cf052..d905dfd1 100644 --- a/src/bsd.h +++ b/src/bsd.h @@ -60,9 +60,6 @@ void mg_bsd_poll(struct mg_mgr *); #endif #if MG_ENABLE_BSD_PROTOTYPES -#if defined(FD_SET) || defined(FD_ZERO) || defined(FD_ISSET) -#error "Include mongoose.h before system select headers" -#endif typedef unsigned int socklen_t; typedef unsigned long nfds_t; typedef uint32_t in_addr_t; @@ -106,6 +103,8 @@ struct pollfd { int fd; short events; short revents; }; #ifndef FD_SETSIZE #define FD_SETSIZE 64 #endif + +#ifndef FD_ZERO #define _NFDBITS ((int) sizeof(__fd_mask) * 8) #define _howmany(x, y) (((x) + ((y) - 1)) / (y)) typedef struct fd_set { @@ -122,6 +121,7 @@ typedef struct fd_set { size_t _n = _howmany(FD_SETSIZE, _NFDBITS); \ while (_n > 0) _p->__fds_bits[--_n] = 0; \ } while (0) +#endif #define h_addr h_addr_list[0] #define AF_INET 2 diff --git a/src/config.h b/src/config.h index 1944fc08..3a397f4e 100644 --- a/src/config.h +++ b/src/config.h @@ -223,10 +223,28 @@ #define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0 #endif +#ifndef MG_TLS_RECORD_SIZE +#define MG_TLS_RECORD_SIZE 16384 // Max plaintext per sent TLS record, builtin +#endif + #ifndef MG_ENABLE_CHACHA20 #define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN #endif +// P-256 implementation, for MG_TLS_BUILTIN +#define MG_P256_LENNGREN 1 // Cortex-M4/M7/M33 asm, see tls_p256_cortex.c +#define MG_P256_BEAR 3 // Portable C, BearSSL, see tls_p256.c + +#ifndef MG_P256 +#if defined(__GNUC__) && defined(__ARM_ARCH_PROFILE) && \ + __ARM_ARCH_PROFILE == 'M' && defined(__ARM_FEATURE_DSP) && \ + !defined(__ARM_BIG_ENDIAN) +#define MG_P256 MG_P256_LENNGREN // Cortex-M with DSP: M4, M7, M33, M55, M85 +#else +#define MG_P256 MG_P256_BEAR +#endif +#endif + #ifndef MG_ENABLE_HEALTH #define MG_ENABLE_HEALTH 0 #endif diff --git a/src/flash.c b/src/flash.c index 758f8552..d9bc3061 100644 --- a/src/flash.c +++ b/src/flash.c @@ -4,7 +4,7 @@ #include "ota.h" #ifdef MG_OTA_PUBLIC_KEY #include "sha256.h" -#include "tls_uecc.h" +#include "tls_p256.h" #endif #if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM @@ -76,9 +76,8 @@ bool mg_ota_flash_end(struct mg_flash *flash) { uint8_t hash[32]; size_t fw_size = s_size - 68; // strip 64-byte sig + 4-byte magic mg_sha256(hash, (uint8_t *) base, fw_size); - ok = mg_uecc_verify(s_pubkey, hash, sizeof(hash), - (uint8_t *) base + fw_size, - mg_uecc_secp256r1()) == 1; + ok = mg_p256_verify(s_pubkey, hash, sizeof(hash), + (uint8_t *) base + fw_size) == 1; MG_INFO(("Signature: %s", ok ? "ok" : "fail")); } else { ok = false; diff --git a/src/jwt.c b/src/jwt.c index f08a9718..c20b5376 100644 --- a/src/jwt.c +++ b/src/jwt.c @@ -7,7 +7,7 @@ #include "util.h" #if MG_TLS == MG_TLS_BUILTIN -#include "tls_uecc.h" +#include "tls_p256.h" #endif static size_t jwt_split(struct mg_str jwt, struct mg_str *h, struct mg_str *p, @@ -95,39 +95,14 @@ size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts, } #if MG_TLS == MG_TLS_BUILTIN -typedef struct { - MG_UECC_HashContext uECC; - mg_sha256_ctx sha256; -} jwt_hash_ctx; - -static void jwt_hash_init(const MG_UECC_HashContext *ctx) { - jwt_hash_ctx *c = (jwt_hash_ctx *) ctx; - mg_sha256_init(&c->sha256); -} - -static void jwt_hash_update(const MG_UECC_HashContext *ctx, const uint8_t *msg, - unsigned len) { - jwt_hash_ctx *c = (jwt_hash_ctx *) ctx; - mg_sha256_update(&c->sha256, msg, len); -} - -static void jwt_hash_finish(const MG_UECC_HashContext *ctx, uint8_t *hash) { - jwt_hash_ctx *c = (jwt_hash_ctx *) ctx; - mg_sha256_final(hash, &c->sha256); -} - size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf, size_t len) { - uint8_t hash[32], sig[64], tmp[2 * 32 + 64]; - jwt_hash_ctx ctx = { - {jwt_hash_init, jwt_hash_update, jwt_hash_finish, 64, 32, tmp}, - {{0}, 0, 0, {0}}}; + uint8_t hash[32], sig[64]; size_t n = jwt_header_payload("ES256", opts, buf, len); size_t m; if (n == 0 || n + 1 >= len) return 0; mg_sha256(hash, (uint8_t *) buf, n); - if (mg_uecc_sign_deterministic(opts->private_key, hash, sizeof(hash), - &ctx.uECC, sig, mg_uecc_secp256r1()) != 1) + if (mg_p256_sign(opts->private_key, hash, sizeof(hash), sig) != 1) return 0; buf[n++] = '.'; m = mg_base64url_encode(sig, sizeof(sig), buf + n, len - n); @@ -143,8 +118,7 @@ size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts, if (mg_base64url_decode(s.buf, s.len, (char *) sig, sizeof(sig)) != 64) return 0; mg_sha256(hash, (uint8_t *) jwt.buf, n); - if (mg_uecc_verify(opts->public_key, hash, sizeof(hash), sig, - mg_uecc_secp256r1()) != 1) + if (mg_p256_verify(opts->public_key, hash, sizeof(hash), sig) != 1) return 0; return mg_base64url_decode(p.buf, p.len, buf, len); } diff --git a/src/mdash.c b/src/mdash.c index 9e58ddaa..8b14ccba 100644 --- a/src/mdash.c +++ b/src/mdash.c @@ -156,9 +156,13 @@ void mg_mdash_init(struct mg_mgr *mgr) { } void mg_mdash_poll(struct mg_mgr *mgr) { +#if MG_TLS == MG_TLS_NONE + (void) mgr, (void) mg_mdash_fn; // mdash needs wss://, nothing to do +#else static uint64_t timer = 0; - // No RPC handlers means mdash was never set up, or mg_mdash_free() ran - if (mg_mdash_conn == NULL && mgr->rpcs != NULL && + // No RPC handlers means mdash was never set up, or mg_mdash_free() ran. + // No key means mdash would reject us, so do not connect at all + if (mg_mdash_conn == NULL && mgr->rpcs != NULL && MG_MDASH_KEY[0] != '\0' && mg_timer_expired(&timer, 1000, mg_millis())) { MG_INFO(("Connecting to %s", MG_MDASH_URL)); mg_mdash_conn = mg_ws_connect(mgr, MG_MDASH_URL, mg_mdash_fn, NULL, @@ -166,6 +170,7 @@ void mg_mdash_poll(struct mg_mgr *mgr) { "Authorization: Bearer %s\r\n", MG_MDASH_URL, MG_MDASH_KEY); } +#endif } void mg_mdash_free(struct mg_mgr *mgr) { diff --git a/src/tls.h b/src/tls.h index 29472bfa..71dc19a3 100644 --- a/src/tls.h +++ b/src/tls.h @@ -90,7 +90,7 @@ void mg_tls_ctx_free(struct mg_mgr *); #if MG_TLS == MG_TLS_BUILTIN // Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE // KEY input. Accepts PEM or DER. Returns key size, or 0 on error. -size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len); +size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len); #endif // Low-level IO primitives used by TLS layer diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 36bb8f09..037cc77f 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -6,8 +6,8 @@ #include "tls_aes128.h" #include "tls_chacha20.h" #include "tls_rsa.h" -#include "tls_uecc.h" -#include "tls_x25519.h" +#include "tls_p256.h" +#include "tls_p384.h" #include "util.h" #if MG_TLS == MG_TLS_BUILTIN @@ -61,6 +61,7 @@ enum mg_tls_hs_state { // Server state machine: MG_TLS_STATE_SERVER_START, // Wait for ClientHello + MG_TLS_STATE_SERVER_RETRY, // Wait for ClientHello after HelloRetry MG_TLS_STATE_SERVER_WAIT_CERT, // Wait for Certificate MG_TLS_STATE_SERVER_WAIT_CV, // Wait for CertificateVerify MG_TLS_STATE_SERVER_NEGOTIATED, // Wait for Finish @@ -106,9 +107,9 @@ struct tls_data { uint8_t random[32]; // client random from ClientHello uint8_t session_id[32]; // client session ID between the handshake states - uint8_t x25519_cli[32]; // client X25519 key between the handshake states uint8_t p256_cli[32]; // client P-256 key between the handshake states - uint8_t x25519_sec[32]; // ECDHE secret (X25519 or P-256) between the states + uint8_t p256_peer[64]; // server: client P-256 public key X || Y + uint8_t ecdhe_sec[32]; // P-256 ECDHE shared secret between the states bool skip_verification; // do not perform checks on server certificate bool cert_requested; // client received a CertificateRequest @@ -132,6 +133,8 @@ struct tls_data { size_t pubkeysz; // size of the server public key uint8_t sighash[32]; // calculated signature verification hash uint8_t sighash384[48]; // same, SHA-384, for ecdsa_secp384r1_sha384 + uint64_t hs_start; // mg_millis() when the handshake started + uint64_t hs_proc; // ms spent processing handshake messages struct tls_enc enc; // actual keys in use at this time struct tls_enc app_keys; // storage during two-way auth handshake @@ -323,7 +326,7 @@ static void mg_tls_derive_secret(const char *label, uint8_t *key, size_t keysz, memmove(hash, secret, hashsz); } -// at this point we have x25519 shared secret, we can generate a set of derived +// at this point we have ECDHE shared secret, we can generate a set of derived // handshake encryption keys static void mg_tls_generate_handshake_keys(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; @@ -344,12 +347,11 @@ static void mg_tls_generate_handshake_keys(struct mg_connection *c) { mg_tls_derive_secret("tls13 derived", early_secret, 32, zeros_sha256_digest, 32, pre_extract_secret, 32); mg_hmac_sha256(tls->enc.handshake_secret, pre_extract_secret, - sizeof(pre_extract_secret), tls->x25519_sec, - sizeof(tls->x25519_sec)); + sizeof(pre_extract_secret), tls->ecdhe_sec, + sizeof(tls->ecdhe_sec)); // ECDHE keys and secret are not needed anymore, wipe for forward secrecy - mg_bzero(tls->x25519_cli, sizeof(tls->x25519_cli)); mg_bzero(tls->p256_cli, sizeof(tls->p256_cli)); - mg_bzero(tls->x25519_sec, sizeof(tls->x25519_sec)); + mg_bzero(tls->ecdhe_sec, sizeof(tls->ecdhe_sec)); mg_tls_hexdump("hs secret", tls->enc.handshake_secret, 32); // mg_sha256_final is not idempotent, need to copy sha256 context to calculate @@ -512,19 +514,20 @@ static bool mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg, static void verbose_alert(uint8_t *buf) { uint8_t level = buf[0], desc = buf[1]; - MG_INFO(("TLS ALERT received: level=%d, desc=%d (%s)", level, desc, - desc == 0 ? "close_notify" - : desc == 10 ? "unexpected_message" - : desc == 20 ? "bad_record_mac" - : desc == 21 ? "decryption_failed" - : desc == 40 ? "handshake_failure" - : desc == 42 ? "bad_certificate" - : desc == 43 ? "unsupported_certificate" - : desc == 44 ? "certificate_revoked" - : desc == 45 ? "certificate_expired" - : desc == 46 ? "certificate_unknown" - : desc == 48 ? "unknown_ca" - : "unknown")); + MG_LOG(desc == 0 ? MG_LL_VERBOSE : MG_LL_INFO, // close_notify is normal + ("TLS ALERT received: level=%d, desc=%d (%s)", level, desc, + desc == 0 ? "close_notify" + : desc == 10 ? "unexpected_message" + : desc == 20 ? "bad_record_mac" + : desc == 21 ? "decryption_failed" + : desc == 40 ? "handshake_failure" + : desc == 42 ? "bad_certificate" + : desc == 43 ? "unsupported_certificate" + : desc == 44 ? "certificate_revoked" + : desc == 45 ? "certificate_expired" + : desc == 46 ? "certificate_unknown" + : desc == 48 ? "unknown_ca" + : "unknown")); } // read an encrypted record, decrypt it in place @@ -659,7 +662,8 @@ static void mg_tls_calc_cert_verify_hash(struct mg_connection *c, if (hash384 != NULL) mg_sha384(hash384, sig_content, sizeof(sig_content)); } -// read and parse ClientHello record +// read and parse ClientHello record. Return 0 if it has a P-256 key share, +// 1 if it does not and HelloRetryRequest must be sent, negative otherwise static int mg_tls_server_recv_hello(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; struct mg_iobuf *rio = &c->rtls; @@ -669,9 +673,13 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) { uint16_t ext_len; uint8_t *ext; uint16_t msgsz; + bool have_p256 = false; + bool is_retry = tls->state == MG_TLS_STATE_SERVER_RETRY; - if (!mg_tls_got_record(c)) { - return MG_IO_WAIT; + for (;;) { + if (!mg_tls_got_record(c)) return MG_IO_WAIT; + if (!is_retry || rio->buf[0] != MG_TLS_CHANGE_CIPHER) break; + mg_tls_drop_record(c); // compatibility mode CCS before 2nd ClientHello } if (rio->buf[0] != MG_TLS_HANDSHAKE || rio->buf[5] != MG_TLS_CLIENT_HELLO) { mg_error(c, "not a client hello packet"); @@ -715,16 +723,21 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) { goto fail; for (k = 0; k < key_exchange_len;) { uint16_t m = MG_LOAD_BE16(key_exchange + k + 2); + uint16_t group = MG_LOAD_BE16(key_exchange + k); if (((uint32_t) m + k + 4) > key_exchange_len) goto fail; - if (m == 32 && key_exchange[k] == 0x00 && key_exchange[k + 1] == 0x1d) { - memmove(tls->x25519_cli, key_exchange + k + 4, m); - mg_tls_drop_record(c); - return 0; + // secp256r1, uncompressed point 0x04 || X || Y + if (group == 0x0017 && m == 65 && key_exchange[k + 4] == 0x04) { + memmove(tls->p256_peer, key_exchange + k + 5, 64); + have_p256 = true; } k += (uint16_t) (m + 4); } j += (uint16_t) (n + 4); } + if (have_p256 || !is_retry) { // No P-256 share: ask for it, but only once + mg_tls_drop_record(c); + return have_p256 ? 0 : 1; + } fail: mg_error(c, "bad client hello"); return -1; @@ -738,58 +751,98 @@ fail: static bool mg_tls_server_send_hello(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; struct mg_iobuf *wio = &tls->send; + uint8_t hello[155], pub[65], prv[32], hdr[5] = {0x16, 0x03, 0x03, 0, 0}; + size_t n = sizeof(hello); // ServerHello length, including 4-byte header - // clang-format off - uint8_t msg_server_hello[122] = { - // server hello, tls 1.2 - 0x02, 0x00, 0x00, 0x76, 0x03, 0x03, - // random (32 bytes) - PLACEHOLDER_32B, - // session ID length + session ID (32 bytes) - 0x20, PLACEHOLDER_32B, -#if MG_ENABLE_CHACHA20 - // TLS_CHACHA20_POLY1305_SHA256 + no compression - 0x13, 0x03, 0x00, -#else - // TLS_AES_128_GCM_SHA256 + no compression - 0x13, 0x01, 0x00, -#endif - // extensions + keyshare - 0x00, 0x2e, 0x00, 0x33, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, - // x25519 keyshare - PLACEHOLDER_32B, - // supported versions (tls1.3 == 0x304) - 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}; - // clang-format on - - // calculate keyshare - uint8_t x25519_pub[X25519_BYTES]; - uint8_t x25519_prv[X25519_BYTES]; - if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG"); - if( mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1) < 0 || mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1) < 0) { + // calculate P-256 keyshare and the shared secret + pub[0] = 0x04; // uncompressed point 0x04 || X || Y + if (!mg_p256_make_key(pub + 1, prv) || + !mg_p256_shared_secret(tls->p256_peer, prv, tls->ecdhe_sec)) { + mg_bzero(prv, sizeof(prv)); mg_error(c, "bad key"); return false; } - mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec)); + mg_bzero(prv, sizeof(prv)); + mg_tls_hexdump("s ecdhe sec", tls->ecdhe_sec, sizeof(tls->ecdhe_sec)); - // fill in the gaps: random + session ID + keyshare - memmove(msg_server_hello + 6, tls->random, sizeof(tls->random)); - memmove(msg_server_hello + 39, tls->session_id, sizeof(tls->session_id)); - memmove(msg_server_hello + 84, x25519_pub, sizeof(x25519_pub)); + hello[0] = 0x02; // server hello, tls 1.2 + MG_STORE_BE24(hello + 1, n - 4); + hello[4] = 0x03, hello[5] = 0x03; + if (!mg_random(hello + 6, 32)) { // server random, RFC 8446 4.1.3 + mg_error(c, "RNG"); + return false; + } + hello[38] = 0x20; // session ID length + session ID (32 bytes) + memmove(hello + 39, tls->session_id, sizeof(tls->session_id)); +#if MG_ENABLE_CHACHA20 + hello[71] = 0x13, hello[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256 +#else + hello[71] = 0x13, hello[72] = 0x01; // TLS_AES_128_GCM_SHA256 +#endif + hello[73] = 0x00; // no compression + MG_STORE_BE16(hello + 74, 65 + 14); // extensions length + MG_STORE_BE16(hello + 76, 0x0033); // key share + MG_STORE_BE16(hello + 78, 65 + 4); + MG_STORE_BE16(hello + 80, 0x0017); // secp256r1 + MG_STORE_BE16(hello + 82, 65); + memmove(hello + 84, pub, sizeof(pub)); + memmove(hello + 149, "\x00\x2b\x00\x02\x03\x04", 6); // tls1.3 == 0x304 + MG_STORE_BE16(hdr + 3, n); // server hello message - if (mg_iobuf_add(wio, wio->len, "\x16\x03\x03\x00\x7a", 5) == 0 || - mg_iobuf_add(wio, wio->len, msg_server_hello, sizeof(msg_server_hello)) == - 0) + if (mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) == 0 || + mg_iobuf_add(wio, wio->len, hello, n) == 0) return false; - mg_sha256_update(&tls->sha256, msg_server_hello, sizeof(msg_server_hello)); + mg_sha256_update(&tls->sha256, hello, n); - // change cipher message - if (mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0) + // change cipher message, unless it was already sent after HelloRetryRequest + if (tls->state != MG_TLS_STATE_SERVER_RETRY && + mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) == 0) return false; return true; } +// HelloRetryRequest random, SHA-256 of "HelloRetryRequest", RFC 8446 4.1.3 +static const uint8_t hrr_random[32] = { + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, + 0x02, 0x1e, 0x65, 0xb8, 0x91, 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, + 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c}; + +// put HelloRetryRequest record into wio buffer, asking for a P-256 key share. +// Replace ClientHello in the transcript with message_hash, RFC 8446 4.4.1 +static bool mg_tls_server_send_hrr(struct mg_connection *c) { + struct tls_data *tls = (struct tls_data *) c->tls; + struct mg_iobuf *wio = &tls->send; + uint8_t hrr[88], hdr[5] = {0x16, 0x03, 0x03, 0, sizeof(hrr)}; + uint8_t mh[4 + 32] = {0xfe, 0, 0, 32}; // message_hash(ClientHello1) + + hrr[0] = 0x02; // server hello, tls 1.2 + MG_STORE_BE24(hrr + 1, sizeof(hrr) - 4); + hrr[4] = 0x03, hrr[5] = 0x03; + memmove(hrr + 6, hrr_random, sizeof(hrr_random)); + hrr[38] = 0x20; // session ID length + session ID (32 bytes) + memmove(hrr + 39, tls->session_id, sizeof(tls->session_id)); +#if MG_ENABLE_CHACHA20 + hrr[71] = 0x13, hrr[72] = 0x03; // TLS_CHACHA20_POLY1305_SHA256 +#else + hrr[71] = 0x13, hrr[72] = 0x01; // TLS_AES_128_GCM_SHA256 +#endif + hrr[73] = 0x00; // no compression + // extensions: supported versions tls1.3, key share secp256r1 (group only) + memmove(hrr + 74, "\x00\x0c\x00\x2b\x00\x02\x03\x04\x00\x33\x00\x02\x00\x17", + 14); + + mg_sha256_final(mh + 4, &tls->sha256); + mg_sha256_init(&tls->sha256); + mg_sha256_update(&tls->sha256, mh, sizeof(mh)); + mg_sha256_update(&tls->sha256, hrr, sizeof(hrr)); + + // HelloRetryRequest message, followed by change cipher message + return mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr)) != 0 && + mg_iobuf_add(wio, wio->len, hrr, sizeof(hrr)) != 0 && + mg_iobuf_add(wio, wio->len, "\x14\x03\x03\x00\x01\x01", 6) != 0; +} + static bool mg_tls_server_send_ext(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; // server extensions @@ -799,14 +852,10 @@ static bool mg_tls_server_send_ext(struct mg_connection *c) { } // signature algorithms we actually support: ecdsa_secp256r1_sha256, -// ecdsa_secp384r1_sha384 (if enabled), rsa_pss_rsae_sha256, rsa_pkcs1_sha256 +// ecdsa_secp384r1_sha384, rsa_pss_rsae_sha256, rsa_pkcs1_sha256 static const uint8_t secp256r1_sig_algs[] = { -#if MG_UECC_SUPPORTS_secp384r1 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03, 0x05, 0x03, 0x08, 0x04, 0x04, 0x01}; -#else - 0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01}; -#endif static bool mg_tls_server_send_cert_request(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; @@ -878,28 +927,6 @@ static bool mg_tls_send_cert(struct mg_connection *c, bool is_client) { return res; } -// type adapter between uECC hash context and our sha256 implementation -typedef struct SHA256_HashContext { - MG_UECC_HashContext uECC; - mg_sha256_ctx ctx; -} SHA256_HashContext; - -static void init_SHA256(const MG_UECC_HashContext *base) { - SHA256_HashContext *c = (SHA256_HashContext *) base; - mg_sha256_init(&c->ctx); -} - -static void update_SHA256(const MG_UECC_HashContext *base, - const uint8_t *message, unsigned message_size) { - SHA256_HashContext *c = (SHA256_HashContext *) base; - mg_sha256_update(&c->ctx, message, message_size); -} -static void finish_SHA256(const MG_UECC_HashContext *base, - uint8_t *hash_result) { - SHA256_HashContext *c = (SHA256_HashContext *) base; - mg_sha256_final(hash_result, &c->ctx); -} - static void mg_tls_mgf1(uint8_t *mask, size_t mask_len, const uint8_t *seed, size_t seed_len) { uint32_t counter = 0; @@ -1142,15 +1169,13 @@ static bool mg_tls_send_cert_verify(struct mg_connection *c, bool is_client) { } else { // EC certificate verify packet uint8_t verify[82] = {0x0f, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00}; - uint8_t tmp[2 * 32 + 64] = {0}; - struct SHA256_HashContext ctx = { - {&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}, - {{0}, 0, 0, {0}}}; size_t sigsz, verifysz = 0; int neg1, neg2; uint8_t sig[64] = {0}; - mg_uecc_sign_deterministic(tls->ec_key, hash, sizeof(hash), &ctx.uECC, sig, - mg_uecc_secp256r1()); + if (!mg_p256_sign(tls->ec_key, hash, sizeof(hash), sig)) { + mg_error(c, "P-256 sign"); + return false; + } neg1 = !!(sig[0] & 0x80); neg2 = !!(sig[32] & 0x80); @@ -1213,7 +1238,6 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; struct mg_iobuf *wio = &tls->send; - uint8_t x25519_pub[X25519_BYTES]; uint8_t p256_pub[64]; // X || Y, sent as an uncompressed point 0x04 || X || Y // - "signature algorithms we actually support", see above @@ -1227,11 +1251,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { 0xfe, 0x00, 0x00, 0xfe}; // clang-format off - uint8_t msg_client_hello[216] = { + uint8_t msg_client_hello[178] = { // TLS Client Hello header reported as TLS1.2 (5) - 0x16, 0x03, 0x03, 0x00, 0xfe, + 0x16, 0x03, 0x03, 0x00, 0xd8, // client hello, tls 1.2 (6) - 0x01, 0x00, 0x00, 0x8c, 0x03, 0x03, + 0x01, 0x00, 0x00, 0xd4, 0x03, 0x03, // random (32 bytes) PLACEHOLDER_32B, // session ID length + session ID (32 bytes) @@ -1248,17 +1272,15 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { 0x01, 0x00, // extensions + keyshare 0x00, 0xfe, - // keyshare: x25519 - 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00, 0x20, - PLACEHOLDER_32B, // keyshare: secp256r1, uncompressed point + 0x00, 0x33, 0x00, 0x47, 0x00, 0x45, 0x00, 0x17, 0x00, 0x41, 0x04, PLACEHOLDER_32B, PLACEHOLDER_32B, - // supported groups (x25519, secp256r1) - 0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x17, + // supported groups (secp256r1) + 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x00, 0x17, // supported versions (tls1.3 == 0x304) 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, // session ticket (none) - 0x00, 0x23, 0x00, 0x00, // 215 bytes till here + 0x00, 0x23, 0x00, 0x00, // 177 bytes till here }; // clang-format on const char *hostname = tls->hostname; @@ -1271,11 +1293,11 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { // patch ClientHello with correct hostname ext length (if any) MG_STORE_BE16(msg_client_hello + 3, - hostname_extsz + 254 - 9 - 34 + sig_alg_sz); + hostname_extsz + 216 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 7, - hostname_extsz + 250 - 9 - 34 + sig_alg_sz); + hostname_extsz + 212 - 9 - 34 + sig_alg_sz); MG_STORE_BE16(msg_client_hello + 82, - hostname_extsz + 175 - 9 - 34 + sig_alg_sz); + hostname_extsz + 137 - 9 - 34 + sig_alg_sz); if (hostnamesz > 0) { MG_STORE_BE16(server_name_ext + 2, hostnamesz + 5); @@ -1283,21 +1305,18 @@ static bool mg_tls_client_send_hello(struct mg_connection *c) { MG_STORE_BE16(server_name_ext + 7, hostnamesz); } - // calculate keyshares - if (!mg_random(tls->x25519_cli, sizeof(tls->x25519_cli))) mg_error(c, "RNG"); - mg_tls_x25519(x25519_pub, tls->x25519_cli, X25519_BASE_POINT, 1); - if (!mg_uecc_make_key(p256_pub, tls->p256_cli, mg_uecc_secp256r1())) { + // calculate keyshare + if (!mg_p256_make_key(p256_pub, tls->p256_cli)) { mg_error(c, "P-256 key"); return false; // Do not send uninitialised p256_pub } - // fill in the gaps: random + session ID + keyshares + // fill in the gaps: random + session ID + keyshare if (!mg_random(tls->session_id, sizeof(tls->session_id))) mg_error(c, "RNG"); if (!mg_random(tls->random, sizeof(tls->random))) mg_error(c, "RNG"); memmove(msg_client_hello + 11, tls->random, sizeof(tls->random)); memmove(msg_client_hello + 44, tls->session_id, sizeof(tls->session_id)); - memmove(msg_client_hello + 94, x25519_pub, sizeof(x25519_pub)); - memmove(msg_client_hello + 131, p256_pub, sizeof(p256_pub)); + memmove(msg_client_hello + 95, p256_pub, sizeof(p256_pub)); // client hello message if (mg_iobuf_add(wio, wio->len, msg_client_hello, sizeof(msg_client_hello)) == @@ -1370,26 +1389,17 @@ static int mg_tls_client_recv_hello(struct mg_connection *c) { key_exchange_len = MG_LOAD_BE16(ext + j + 6); key_exchange = ext + j + 8; if (key_exchange_len > ext_len2 - 4) goto fail; - if (group == 0x001d) { // x25519 - if (key_exchange_len != 32 || - mg_tls_x25519(tls->x25519_sec, tls->x25519_cli, key_exchange, 1) < - 0) { - mg_error(c, "bad key"); - return -1; - } - } else if (group == 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y - if (key_exchange_len != 65 || key_exchange[0] != 0x04 || - !mg_uecc_valid_public_key(key_exchange + 1, mg_uecc_secp256r1()) || - !mg_uecc_shared_secret(key_exchange + 1, tls->p256_cli, - tls->x25519_sec, mg_uecc_secp256r1())) { - mg_error(c, "bad key"); - return -1; - } - } else { + if (group != 0x0017) { // secp256r1, uncompressed point 0x04 || X || Y mg_error(c, "bad key exchange group"); return -1; } - // mg_tls_hexdump("c ecdhe sec", tls->x25519_sec, 32); + if (key_exchange_len != 65 || key_exchange[0] != 0x04 || + !mg_p256_shared_secret(key_exchange + 1, tls->p256_cli, + tls->ecdhe_sec)) { // Validates the point + mg_error(c, "bad key"); + return -1; + } + // mg_tls_hexdump("c ecdhe sec", tls->ecdhe_sec, 32); mg_tls_drop_record(c); /* generate handshake keys */ mg_tls_generate_handshake_keys(c); @@ -1714,7 +1724,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, MG_ERROR(("cert verification error")); return 0; } -#if MG_UECC_SUPPORTS_secp256r1 if (issuer->pubkey.len == 64) { const uint32_t N = 32; if (a.len > N) a.value += (a.len - N), a.len = N; // padding @@ -1723,13 +1732,9 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, memmove(sig + (N - a.len), a.value, a.len); memset(sig + N, 0, N - b.len); memmove(sig + N + (N - b.len), b.value, b.len); - return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, - (unsigned) cert->tbshashsz, sig, - mg_uecc_secp256r1()); - } else -#endif -#if MG_UECC_SUPPORTS_secp384r1 - if (issuer->pubkey.len == 96) { + return mg_p256_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, + cert->tbshashsz, sig); + } else if (issuer->pubkey.len == 96) { const uint32_t N = 48; if (a.len > N) a.value += (a.len - N), a.len = N; // padding if (b.len > N) b.value += (b.len - N), b.len = N; @@ -1737,12 +1742,9 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, memmove(sig + (N - a.len), a.value, a.len); memset(sig + N, 0, N - b.len); memmove(sig + N + (N - b.len), b.value, b.len); - return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, - (unsigned) cert->tbshashsz, sig, - mg_uecc_secp384r1()); - } else -#endif - { + return mg_p384_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash, + cert->tbshashsz, sig); + } else { MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len)); return 0; } @@ -2040,8 +2042,7 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) { return -1; } MG_VERBOSE(("certificate verification successful (RSA)")); - } else if (sigalg == 0x0403 || - (sigalg == 0x0503 && MG_UECC_SUPPORTS_secp384r1)) { + } else if (sigalg == 0x0403 || sigalg == 0x0503) { // ecdsa_secp256r1_sha256 or ecdsa_secp384r1_sha384. Extract certificate // signature and verify it using pubkey and sighash bool is384 = sigalg == 0x0503; @@ -2074,15 +2075,12 @@ static int mg_tls_recv_cert_verify(struct mg_connection *c) { memmove(sig + (N - r.len), r.value, r.len); memmove(sig + N + (N - s.len), s.value, s.len); -#if MG_UECC_SUPPORTS_secp384r1 if (is384) { - ok = mg_uecc_verify(tls->pubkey, tls->sighash384, - sizeof(tls->sighash384), sig, mg_uecc_secp384r1()); - } else -#endif - { - ok = mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), - sig, mg_uecc_secp256r1()); + ok = mg_p384_verify(tls->pubkey, tls->sighash384, + sizeof(tls->sighash384), sig); + } else { + ok = mg_p256_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), + sig); } if (ok != 1) { mg_error(c, "failed to verify EC certificate (certverify)"); @@ -2186,16 +2184,23 @@ static bool mg_tls_client_handshake(struct mg_connection *c) { static bool mg_tls_server_handshake(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; + int r; switch (tls->state) { case MG_TLS_STATE_SERVER_START: - if (mg_tls_server_recv_hello(c) < 0) break; + case MG_TLS_STATE_SERVER_RETRY: + if ((r = mg_tls_server_recv_hello(c)) < 0) break; + if (r == 1) { // No P-256 key share, ask for it + if (!mg_tls_server_send_hrr(c)) return false; + tls->state = MG_TLS_STATE_SERVER_RETRY; + break; + } if (!mg_tls_server_send_hello(c)) return false; mg_tls_generate_handshake_keys(c); if (!mg_tls_server_send_ext(c)) return false; if (tls->is_twoway && !mg_tls_server_send_cert_request(c)) return false; - if (!mg_tls_send_cert(c, false) || !mg_tls_send_cert_verify(c, false) || - !mg_tls_server_send_finish(c)) - return false; + if (!mg_tls_send_cert(c, false)) return false; + if (!mg_tls_send_cert_verify(c, false)) return false; + if (!mg_tls_server_send_finish(c)) return false; if (tls->is_twoway) { // generate application keys at this point, keep using handshake keys struct tls_enc hs_keys = tls->enc; @@ -2236,14 +2241,25 @@ void mg_tls_handshake(struct mg_connection *c) { struct tls_data *tls = (struct tls_data *) c->tls; long n; bool res; + uint64_t t; if (c->is_closing) return; // we don't clear rx buf, so ignore what's left if (tls->is_sntp_pending) return; + t = mg_millis(); if (c->is_client) { // will clear is_hs when sending last chunk res = mg_tls_client_handshake(c); } else { res = mg_tls_server_handshake(c); } + tls->hs_proc += mg_millis() - t; + if (res && !c->is_tls_hs) { + // CPU: key exchange, signing, certificate verification. Wait: the rest of + // the total, waiting for the peer and the network + unsigned long total = (unsigned long) (mg_millis() - tls->hs_start); + unsigned long cpu = (unsigned long) tls->hs_proc; + MG_DEBUG(("%lu TLS handshake done in %lu ms: %lu ms CPU, %lu ms peer wait", + c->id, total, cpu, total > cpu ? total - cpu : 0)); + } if (!res) { mg_error(c, "TLS OOM"); return; @@ -2662,7 +2678,7 @@ static int mg_parse_pem_certs(const struct mg_str pem, struct mg_str **ders) { return count; } -size_t mg_uecc_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) { +size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len) { struct mg_str der = mg_str_n(NULL, 0); size_t n = 0; if (buf == NULL || len < 32) return 0; @@ -2702,6 +2718,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) { mg_error(c, "tls oom"); return; } + tls->hs_start = mg_millis(); tls->state = c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START; @@ -2887,11 +2904,15 @@ long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { long n = MG_IO_WAIT; bool was_throttled = c->is_tls_throttled; // see #3074 if (!was_throttled) { // encrypt new data + size_t i, rs = MG_TLS_RECORD_SIZE > 16384 ? 16384 : MG_TLS_RECORD_SIZE; if (len > MG_IO_SIZE) len = MG_IO_SIZE; - if (len > 16384) len = 16384; - if (!mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA)) - return 0; // returning 0 means an OOM condition (iobuf couldn't resize), - // yet this is so far recoverable, let the caller decide + for (i = 0; i < len; i += rs) { // Split into records, send them at once + size_t m = len - i < rs ? len - i : rs; + if (!mg_tls_encrypt(c, (const uint8_t *) buf + i, m, MG_TLS_APP_DATA)) + return 0; // returning 0 means an OOM condition (iobuf couldn't + // resize), yet this is so far recoverable, let the caller + // decide + } } // else, resend outstanding encrypted data in tls->send while (tls->send.len > 0 && (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { diff --git a/src/tls_p256.c b/src/tls_p256.c new file mode 100644 index 00000000..cb8ab120 --- /dev/null +++ b/src/tls_p256.c @@ -0,0 +1,2553 @@ +// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by +// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN) +// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA +// code is always compiled, it is also used by P-384, see tls_p384.c +// +// Copyright (c) 2016 Thomas Pornin +// Copyright (c) 2017 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the +// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not +// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is +// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and +// public key directly +#include "tls_p256.h" +#include "sha256.h" +#include "util.h" + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + +// From BearSSL src/codec/ccopy.c +void +mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = (unsigned char *)dst; + s = (const unsigned char *)src; + while (len -- > 0) { + uint32_t x, y; + + x = *s ++; + y = *d; + *d = (unsigned char)MG_BR_MUX(ctl, x, y); + d ++; + } +} + +// From BearSSL src/int/i32_div32.c +uint32_t +mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r) +{ + /* TODO: optimize this */ + uint32_t q; + uint32_t ch, cf; + int k; + + q = 0; + ch = MG_BR_EQ(hi, d); + hi = MG_BR_MUX(ch, 0, hi); + for (k = 31; k > 0; k --) { + int j; + uint32_t w, ctl, hi2, lo2; + + j = 32 - k; + w = (hi << j) | (lo >> k); + ctl = MG_BR_GE(w, d) | (hi >> k); + hi2 = (w - d) >> j; + lo2 = lo - (d << k); + hi = MG_BR_MUX(ctl, hi2, hi); + lo = MG_BR_MUX(ctl, lo2, lo); + q |= ctl << k; + } + cf = MG_BR_GE(lo, d) | hi; + q |= cf; + *r = MG_BR_MUX(cf, lo - d, lo); + return q; +} + +// From BearSSL src/int/i31_add.c +uint32_t +mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 63) >> 5; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = b[u]; + naw = aw + bw + cc; + cc = naw >> 31; + a[u] = MG_BR_MUX(ctl, naw & (uint32_t)0x7FFFFFFF, aw); + } + return cc; +} + +// From BearSSL src/int/i31_bitlen.c +uint32_t +mg_br_i31_bit_length(uint32_t *x, size_t xlen) +{ + uint32_t tw, twk; + + tw = 0; + twk = 0; + while (xlen -- > 0) { + uint32_t w, c; + + c = MG_BR_EQ(tw, 0); + w = x[xlen]; + tw = MG_BR_MUX(c, w, tw); + twk = MG_BR_MUX(c, (uint32_t)xlen, twk); + } + return (twk << 5) + MG_BR_BIT_LENGTH(tw); +} + +// From BearSSL src/int/i31_decmod.c +uint32_t +mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len, const uint32_t *m) +{ + /* + * Two-pass algorithm: in the first pass, we determine whether the + * value fits; in the second pass, we do the actual write. + * + * During the first pass, 'r' contains the comparison result so + * far: + * 0x00000000 value is equal to the modulus + * 0x00000001 value is greater than the modulus + * 0xFFFFFFFF value is lower than the modulus + * + * Since we iterate starting with the least significant bytes (at + * the end of src[]), each new comparison overrides the previous + * except when the comparison yields 0 (equal). + * + * During the second pass, 'r' is either 0xFFFFFFFF (value fits) + * or 0x00000000 (value does not fit). + * + * We must iterate over all bytes of the source, _and_ possibly + * some extra virtual bytes (with value 0) so as to cover the + * complete modulus as well. We also add 4 such extra bytes beyond + * the modulus length because it then guarantees that no accumulated + * partial word remains to be processed. + */ + const unsigned char *buf; + size_t mlen, tlen; + int pass; + uint32_t r; + + buf = (const unsigned char *)src; + mlen = (m[0] + 31) >> 5; + tlen = (mlen << 2); + if (tlen < len) { + tlen = len; + } + tlen += 4; + r = 0; + for (pass = 0; pass < 2; pass ++) { + size_t u, v; + uint32_t acc; + int acc_len; + + v = 1; + acc = 0; + acc_len = 0; + for (u = 0; u < tlen; u ++) { + uint32_t b; + + if (u < len) { + b = buf[len - 1 - u]; + } else { + b = 0; + } + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 31) { + uint32_t xw; + + xw = acc & (uint32_t)0x7FFFFFFF; + acc_len -= 31; + acc = b >> (8 - acc_len); + if (v <= mlen) { + if (pass) { + x[v] = r & xw; + } else { + uint32_t cc; + + cc = (uint32_t)MG_BR_CMP(xw, m[v]); + r = MG_BR_MUX(MG_BR_EQ(cc, 0), r, cc); + } + } else { + if (!pass) { + r = MG_BR_MUX(MG_BR_EQ(xw, 0), r, 1); + } + } + v ++; + } + } + + /* + * When we reach this point at the end of the first pass: + * r is either 0, 1 or -1; we want to set r to 0 if it + * is equal to 0 or 1, and leave it to -1 otherwise. + * + * When we reach this point at the end of the second pass: + * r is either 0 or -1; we want to leave that value + * untouched. This is a subcase of the previous. + */ + r >>= 1; + r |= (r << 1); + } + + x[0] = m[0]; + return r & (uint32_t)1; +} + +// From BearSSL src/int/i31_decode.c +void +mg_br_i31_decode(uint32_t *x, const void *src, size_t len) +{ + const unsigned char *buf; + size_t u, v; + uint32_t acc; + int acc_len; + + buf = (const unsigned char *)src; + u = len; + v = 1; + acc = 0; + acc_len = 0; + while (u -- > 0) { + uint32_t b; + + b = buf[u]; + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 31) { + x[v ++] = acc & (uint32_t)0x7FFFFFFF; + acc_len -= 31; + acc = b >> (8 - acc_len); + } + } + if (acc_len != 0) { + x[v ++] = acc; + } + x[0] = mg_br_i31_bit_length(x + 1, v - 1); +} + +// From BearSSL src/int/i31_encode.c +void +mg_br_i31_encode(void *dst, size_t len, const uint32_t *x) +{ + unsigned char *buf; + size_t k, xlen; + uint32_t acc; + int acc_len; + + xlen = (x[0] + 31) >> 5; + if (xlen == 0) { + memset(dst, 0, len); + return; + } + buf = (unsigned char *)dst + len; + k = 1; + acc = 0; + acc_len = 0; + while (len != 0) { + uint32_t w; + + w = (k <= xlen) ? x[k] : 0; + k ++; + if (acc_len == 0) { + acc = w; + acc_len = 31; + } else { + uint32_t z; + + z = acc | (w << acc_len); + acc_len --; + acc = w >> (31 - acc_len); + if (len >= 4) { + buf -= 4; + len -= 4; + mg_br_enc32be(buf, z); + } else { + switch (len) { + case 3: + buf[-3] = (unsigned char)(z >> 16); + /* fall through */ + case 2: + buf[-2] = (unsigned char)(z >> 8); + /* fall through */ + case 1: + buf[-1] = (unsigned char)z; + break; + } + return; + } + } + } +} + +// From BearSSL src/int/i31_fmont.c +void +mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i) +{ + size_t len, u, v; + + len = (m[0] + 31) >> 5; + for (u = 0; u < len; u ++) { + uint32_t f; + uint64_t cc; + + f = MG_BR_MUL31_lo(x[1], m0i); + cc = 0; + for (v = 0; v < len; v ++) { + uint64_t z; + + z = (uint64_t)x[v + 1] + MG_BR_MUL31(f, m[v + 1]) + cc; + cc = z >> 31; + if (v != 0) { + x[v] = (uint32_t)z & 0x7FFFFFFF; + } + } + x[len] = (uint32_t)cc; + } + + /* + * We may have to do an extra subtraction, but only if the + * value in x[] is indeed greater than or equal to that of m[], + * which is why we must do two calls (first call computes the + * carry, second call performs the subtraction only if the carry + * is 0). + */ + mg_br_i31_sub(x, m, MG_BR_NOT(mg_br_i31_sub(x, m, 0))); +} + +// From BearSSL src/int/i31_iszero.c +uint32_t +mg_br_i31_iszero(const uint32_t *x) +{ + uint32_t z; + size_t u; + + z = 0; + for (u = (x[0] + 31) >> 5; u > 0; u --) { + z |= x[u]; + } + return ~(z | -z) >> 31; +} + +// From BearSSL src/int/i31_modpow.c +void +mg_br_i31_modpow(uint32_t *x, + const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2) +{ + size_t mlen; + uint32_t k; + + /* + * 'mlen' is the length of m[] expressed in bytes (including + * the "bit length" first field). + */ + mlen = ((m[0] + 63) >> 5) * sizeof m[0]; + + /* + * Throughout the algorithm: + * -- t1[] is in Montgomery representation; it contains x, x^2, + * x^4, x^8... + * -- The result is accumulated, in normal representation, in + * the x[] array. + * -- t2[] is used as destination buffer for each multiplication. + * + * Note that there is no need to call mg_br_i32_from_monty(). + */ + memcpy(t1, x, mlen); + mg_br_i31_to_monty(t1, m); + mg_br_i31_zero(x, m[0]); + x[1] = 1; + for (k = 0; k < ((uint32_t)elen << 3); k ++) { + uint32_t ctl; + + ctl = (e[elen - 1 - (k >> 3)] >> (k & 7)) & 1; + mg_br_i31_montymul(t2, x, t1, m, m0i); + MG_BR_CCOPY(ctl, x, t2, mlen); + mg_br_i31_montymul(t2, t1, t1, m, m0i); + memcpy(t1, t2, mlen); + } +} + +// From BearSSL src/int/i31_montmul.c +void +mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i) +{ + /* + * Each outer loop iteration computes: + * d <- (d + xu*y + f*m) / 2^31 + * We have xu <= 2^31-1 and f <= 2^31-1. + * Thus, if d <= 2*m-1 on input, then: + * 2*m-1 + 2*(2^31-1)*m <= (2^32)*m-1 + * and the new d value is less than 2*m. + * + * We represent d over 31-bit words, with an extra word 'dh' + * which can thus be only 0 or 1. + */ + size_t len, len4, u, v; + uint32_t dh; + + len = (m[0] + 31) >> 5; + len4 = len & ~(size_t)3; + mg_br_i31_zero(d, m[0]); + dh = 0; + for (u = 0; u < len; u ++) { + /* + * The carry for each operation fits on 32 bits: + * d[v+1] <= 2^31-1 + * xu*y[v+1] <= (2^31-1)*(2^31-1) + * f*m[v+1] <= (2^31-1)*(2^31-1) + * r <= 2^32-1 + * (2^31-1) + 2*(2^31-1)*(2^31-1) + (2^32-1) = 2^63 - 2^31 + * After division by 2^31, the new r is then at most 2^32-1 + * + * Using a 32-bit carry has performance benefits on 32-bit + * systems; however, on 64-bit architectures, we prefer to + * keep the carry (r) in a 64-bit register, thus avoiding some + * "clear high bits" operations. + */ + uint32_t f, xu; + uint32_t r; /* Mongoose: 32-bit carry only */ + + xu = x[u + 1]; + f = MG_BR_MUL31_lo((d[1] + MG_BR_MUL31_lo(x[u + 1], y[1])), m0i); + + r = 0; + for (v = 0; v < len4; v += 4) { + uint64_t z; + + z = (uint64_t)d[v + 1] + MG_BR_MUL31(xu, y[v + 1]) + + MG_BR_MUL31(f, m[v + 1]) + r; + r = (uint32_t)(z >> 31); + d[v + 0] = (uint32_t)z & 0x7FFFFFFF; + z = (uint64_t)d[v + 2] + MG_BR_MUL31(xu, y[v + 2]) + + MG_BR_MUL31(f, m[v + 2]) + r; + r = (uint32_t)(z >> 31); + d[v + 1] = (uint32_t)z & 0x7FFFFFFF; + z = (uint64_t)d[v + 3] + MG_BR_MUL31(xu, y[v + 3]) + + MG_BR_MUL31(f, m[v + 3]) + r; + r = (uint32_t)(z >> 31); + d[v + 2] = (uint32_t)z & 0x7FFFFFFF; + z = (uint64_t)d[v + 4] + MG_BR_MUL31(xu, y[v + 4]) + + MG_BR_MUL31(f, m[v + 4]) + r; + r = (uint32_t)(z >> 31); + d[v + 3] = (uint32_t)z & 0x7FFFFFFF; + } + for (; v < len; v ++) { + uint64_t z; + + z = (uint64_t)d[v + 1] + MG_BR_MUL31(xu, y[v + 1]) + + MG_BR_MUL31(f, m[v + 1]) + r; + r = (uint32_t)(z >> 31); + d[v] = (uint32_t)z & 0x7FFFFFFF; + } + + /* + * Since the new dh can only be 0 or 1, the addition of + * the old dh with the carry MUST fit on 32 bits, and + * thus can be done into dh itself. + */ + dh += r; + d[len] = dh & 0x7FFFFFFF; + dh >>= 31; + } + + /* + * We must write back the bit length because it was overwritten in + * the loop (not overwriting it would require a test in the loop, + * which would yield bigger and slower code). + */ + d[0] = m[0]; + + /* + * d[] may still be greater than m[] at that point; notably, the + * 'dh' word may be non-zero. + */ + mg_br_i31_sub(d, m, MG_BR_NEQ(dh, 0) | MG_BR_NOT(mg_br_i31_sub(d, m, 0))); +} + +// From BearSSL src/int/i31_muladd.c +void +mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m) +{ + uint32_t m_bitlen; + unsigned mblr; + size_t u, mlen; + uint32_t a0, a1, b0, hi, g, q, tb; + uint32_t under, over; + uint32_t cc; + + /* + * We can test on the modulus bit length since we accept to + * leak that length. + */ + m_bitlen = m[0]; + if (m_bitlen == 0) { + return; + } + if (m_bitlen <= 31) { + uint32_t lo; + + hi = x[1] >> 1; + lo = (x[1] << 31) | z; + x[1] = mg_br_rem(hi, lo, m[1]); + return; + } + mlen = (m_bitlen + 31) >> 5; + mblr = (unsigned)m_bitlen & 31; + + /* + * Principle: we estimate the quotient (x*2^31+z)/m by + * doing a 64/32 division with the high words. + * + * Let: + * w = 2^31 + * a = (w*a0 + a1) * w^N + a2 + * b = b0 * w^N + b2 + * such that: + * 0 <= a0 < w + * 0 <= a1 < w + * 0 <= a2 < w^N + * w/2 <= b0 < w + * 0 <= b2 < w^N + * a < w*b + * I.e. the two top words of a are a0:a1, the top word of b is + * b0, we ensured that b0 is "full" (high bit set), and a is + * such that the quotient q = a/b fits on one word (0 <= q < w). + * + * If a = b*q + r (with 0 <= r < q), we can estimate q by + * doing an Euclidean division on the top words: + * a0*w+a1 = b0*u + v (with 0 <= v < b0) + * Then the following holds: + * 0 <= u <= w + * u-2 <= q <= u + */ + hi = x[mlen]; + if (mblr == 0) { + a0 = x[mlen]; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a1 = x[mlen]; + b0 = m[mlen]; + } else { + a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) + & 0x7FFFFFFF; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) + & 0x7FFFFFFF; + b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr)) + & 0x7FFFFFFF; + } + + /* + * We estimate a divisor q. If the quotient returned by mg_br_div() + * is g: + * -- If a0 == b0 then g == 0; we want q = 0x7FFFFFFF. + * -- Otherwise: + * -- if g == 0 then we set q = 0; + * -- otherwise, we set q = g - 1. + * The properties described above then ensure that the true + * quotient is q-1, q or q+1. + * + * Take care that a0, a1 and b0 are 31-bit words, not 32-bit. We + * must adjust the parameters to mg_br_div() accordingly. + */ + g = mg_br_div(a0 >> 1, a1 | (a0 << 31), b0); + q = MG_BR_MUX(MG_BR_EQ(a0, b0), 0x7FFFFFFF, MG_BR_MUX(MG_BR_EQ(g, 0), 0, g - 1)); + + /* + * We subtract q*m from x (with the extra high word of value 'hi'). + * Since q may be off by 1 (in either direction), we may have to + * add or subtract m afterwards. + * + * The 'tb' flag will be true (1) at the end of the loop if the + * result is greater than or equal to the modulus (not counting + * 'hi' or the carry). + */ + cc = 0; + tb = 1; + for (u = 1; u <= mlen; u ++) { + uint32_t mw, zw, xw, nxw; + uint64_t zl; + + mw = m[u]; + zl = MG_BR_MUL31(mw, q) + cc; + cc = (uint32_t)(zl >> 31); + zw = (uint32_t)zl & (uint32_t)0x7FFFFFFF; + xw = x[u]; + nxw = xw - zw; + cc += nxw >> 31; + nxw &= 0x7FFFFFFF; + x[u] = nxw; + tb = MG_BR_MUX(MG_BR_EQ(nxw, mw), tb, MG_BR_GT(nxw, mw)); + } + + /* + * If we underestimated q, then either cc < hi (one extra bit + * beyond the top array word), or cc == hi and tb is true (no + * extra bit, but the result is not lower than the modulus). In + * these cases we must subtract m once. + * + * Otherwise, we may have overestimated, which will show as + * cc > hi (thus a negative result). Correction is adding m once. + */ + over = MG_BR_GT(cc, hi); + under = ~over & (tb | MG_BR_LT(cc, hi)); + mg_br_i31_add(x, m, over); + mg_br_i31_sub(x, m, under); +} + +// From BearSSL src/int/i31_ninv31.c +uint32_t +mg_br_i31_ninv31(uint32_t x) +{ + uint32_t y; + + y = 2 - x; + y *= 2 - y * x; + y *= 2 - y * x; + y *= 2 - y * x; + y *= 2 - y * x; + return MG_BR_MUX(x & 1, -y, 0) & 0x7FFFFFFF; +} + +// From BearSSL src/int/i31_rshift.c +void +mg_br_i31_rshift(uint32_t *x, int count) +{ + size_t u, len; + uint32_t r; + + len = (x[0] + 31) >> 5; + if (len == 0) { + return; + } + r = x[1] >> count; + for (u = 2; u <= len; u ++) { + uint32_t w; + + w = x[u]; + x[u - 1] = ((w << (31 - count)) | r) & 0x7FFFFFFF; + r = w >> count; + } + x[len] = r; +} + +// From BearSSL src/int/i31_sub.c +uint32_t +mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 63) >> 5; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = b[u]; + naw = aw - bw - cc; + cc = naw >> 31; + a[u] = MG_BR_MUX(ctl, naw & 0x7FFFFFFF, aw); + } + return cc; +} + +// From BearSSL src/int/i31_tmont.c +void +mg_br_i31_to_monty(uint32_t *x, const uint32_t *m) +{ + uint32_t k; + + for (k = (m[0] + 31) >> 5; k > 0; k --) { + mg_br_i31_muladd_small(x, 0, m); + } +} + +// From BearSSL src/ec/ecdsa_i31_bits.c +void +mg_br_ecdsa_i31_bits2int(uint32_t *x, + const void *src, size_t len, uint32_t ebitlen) +{ + uint32_t bitlen, hbitlen; + int sc; + + bitlen = ebitlen - (ebitlen >> 5); + hbitlen = (uint32_t)len << 3; + if (hbitlen > bitlen) { + len = (bitlen + 7) >> 3; + sc = (int)((hbitlen - bitlen) & 7); + } else { + sc = 0; + } + mg_br_i31_zero(x, ebitlen); + mg_br_i31_decode(x, src, len); + mg_br_i31_rshift(x, sc); + x[0] = ebitlen; +} + +// From BearSSL src/ec/ecdsa_i31_vrfy_raw.c +#define I31_LEN ((MG_BR_MAX_EC_SIZE + 61) / 31) +#define POINT_LEN (1 + (((MG_BR_MAX_EC_SIZE + 7) >> 3) << 1)) + +uint32_t +mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash, size_t hash_len, + const unsigned char *q, size_t qlen, + const void *sig, size_t sig_len) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + */ + const unsigned char *order; + uint32_t n[I31_LEN], r[I31_LEN], s[I31_LEN], t1[I31_LEN], t2[I31_LEN]; + unsigned char tx[(MG_BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char ty[(MG_BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char eU[POINT_LEN]; + size_t nlen, rlen, ulen; + uint32_t n0i, res; + + /* + * If the curve is not supported, then report an error. + */ + if (((impl->supported_curves >> curve) & 1) == 0) { + return 0; + } + + /* + * Get the curve parameters (generator and order). + */ + order = impl->order(curve, &nlen); + impl->generator(curve, &ulen); + + /* + * Signature length must be even. + */ + if (sig_len & 1) { + return 0; + } + rlen = sig_len >> 1; + + /* + * Public key point must have the proper size for this curve. + */ + if (qlen != ulen) { + return 0; + } + + /* + * Get modulus; then decode the r and s values. They must be + * lower than the modulus, and s must not be null. + */ + mg_br_i31_decode(n, order, nlen); + n0i = mg_br_i31_ninv31(n[1]); + if (!mg_br_i31_decode_mod(r, sig, rlen, n)) { + return 0; + } + if (!mg_br_i31_decode_mod(s, (const unsigned char *)sig + rlen, rlen, n)) { + return 0; + } + if (mg_br_i31_iszero(s)) { + return 0; + } + + /* + * Invert s. We do that with a modular exponentiation; we use + * the fact that for all the curves we support, the least + * significant byte is not 0 or 1, so we can subtract 2 without + * any carry to process. + * We also want 1/s in Montgomery representation, which can be + * done by converting _from_ Montgomery representation before + * the inversion (because (1/s)*R = 1/(s/R)). + */ + mg_br_i31_from_monty(s, n, n0i); + memcpy(tx, order, nlen); + tx[nlen - 1] -= 2; + mg_br_i31_modpow(s, tx, nlen, n, n0i, t1, t2); + + /* + * Truncate the hash to the modulus length (in bits) and reduce + * it modulo the curve order. The modular reduction can be done + * with a subtraction since the truncation already reduced the + * value to the modulus bit length. + */ + mg_br_ecdsa_i31_bits2int(t1, hash, hash_len, n[0]); + mg_br_i31_sub(t1, n, mg_br_i31_sub(t1, n, 0) ^ 1); + + /* + * Multiply the (truncated, reduced) hash value with 1/s, result in + * t2, encoded in ty. + */ + mg_br_i31_montymul(t2, t1, s, n, n0i); + mg_br_i31_encode(ty, nlen, t2); + + /* + * Multiply r with 1/s, result in t1, encoded in tx. + */ + mg_br_i31_montymul(t1, r, s, n, n0i); + mg_br_i31_encode(tx, nlen, t1); + + /* + * Compute the point x*Q + y*G. + */ + memcpy(eU, q, ulen); + res = impl->muladd(eU, NULL, ulen, + tx, nlen, ty, nlen, curve); + + /* + * Get the X coordinate, reduce modulo the curve order, and + * compare with the 'r' value. + * + * The modular reduction can be done with subtractions because + * we work with curves of prime order, so the curve order is + * close to the field order (Hasse's theorem). + */ + mg_br_i31_zero(t1, n[0]); + mg_br_i31_decode(t1, &eU[1], ulen >> 1); + t1[0] = n[0]; + mg_br_i31_sub(t1, n, mg_br_i31_sub(t1, n, 0) ^ 1); + res &= ~mg_br_i31_sub(t1, r, 1); + res &= mg_br_i31_iszero(t1); + return res; +} + +// From BearSSL src/ec/ecdsa_i31_sign_raw.c, k is given by the caller +#define ORDER_LEN ((MG_BR_MAX_EC_SIZE + 7) >> 3) + +size_t +mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash_value, size_t hash_len, + const unsigned char *xb, size_t xlen, + const unsigned char *kb, void *sig) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + * We also rely on the last byte of the curve order to be distinct + * from 0 and 1. + */ + const unsigned char *order; + uint32_t n[I31_LEN], r[I31_LEN], s[I31_LEN], x[I31_LEN]; + uint32_t m[I31_LEN], k[I31_LEN], t1[I31_LEN], t2[I31_LEN]; + unsigned char tt[ORDER_LEN << 1]; + unsigned char eU[POINT_LEN]; + size_t nlen, ulen; + uint32_t n0i, ctl; + + /* + * If the curve is not supported, then exit with an error. + */ + if (((impl->supported_curves >> curve) & 1) == 0) { + return 0; + } + + /* + * Get modulus. + */ + order = impl->order(curve, &nlen); + mg_br_i31_decode(n, order, nlen); + n0i = mg_br_i31_ninv31(n[1]); + + /* + * Get private key as an i31 integer. This also checks that the + * private key is well-defined (not zero, and less than the + * curve order). + */ + if (!mg_br_i31_decode_mod(x, xb, xlen, n)) { + return 0; + } + if (mg_br_i31_iszero(x)) { + return 0; + } + + /* + * Truncate and reduce the hash value modulo the curve order. + */ + mg_br_ecdsa_i31_bits2int(m, hash_value, hash_len, n[0]); + mg_br_i31_sub(m, n, mg_br_i31_sub(m, n, 0) ^ 1); + + /* + * Mongoose: the "k" value is given by the caller (e.g. RFC 6979, + * computed by uECC). It must be in [1, n-1]. + */ + if (!mg_br_i31_decode_mod(k, kb, nlen, n) || mg_br_i31_iszero(k)) { + return 0; + } + + /* + * Compute k*G and extract the X coordinate, then reduce it + * modulo the curve order. Since we support only curves with + * prime order, that reduction is only a matter of computing + * a subtraction. + */ + mg_br_i31_encode(tt, nlen, k); + ulen = impl->mulgen(eU, tt, nlen, curve); + mg_br_i31_zero(r, n[0]); + mg_br_i31_decode(r, &eU[1], ulen >> 1); + r[0] = n[0]; + mg_br_i31_sub(r, n, mg_br_i31_sub(r, n, 0) ^ 1); + + /* + * Compute 1/k in double-Montgomery representation. We do so by + * first converting _from_ Montgomery representation (twice), + * then using a modular exponentiation. + */ + mg_br_i31_from_monty(k, n, n0i); + mg_br_i31_from_monty(k, n, n0i); + memcpy(tt, order, nlen); + tt[nlen - 1] -= 2; + mg_br_i31_modpow(k, tt, nlen, n, n0i, t1, t2); + + /* + * Compute s = (m+xr)/k (mod n). + * The k[] array contains R^2/k (double-Montgomery representation); + * we thus can use direct Montgomery multiplications and conversions + * from Montgomery, avoiding any call to mg_br_i31_to_monty() (which + * is slower). + */ + mg_br_i31_from_monty(m, n, n0i); + mg_br_i31_montymul(t1, x, r, n, n0i); + ctl = mg_br_i31_add(t1, m, 1); + ctl |= mg_br_i31_sub(t1, n, 0) ^ 1; + mg_br_i31_sub(t1, n, ctl); + mg_br_i31_montymul(s, t1, k, n, n0i); + + /* + * Encode r and s in the signature. + */ + mg_br_i31_encode(sig, nlen, r); + mg_br_i31_encode((unsigned char *)sig + nlen, nlen, s); + return nlen << 1; +} + +#if MG_P256 == MG_P256_BEAR +// From BearSSL src/ec/ec_p256_m31.c +/* + * If MG_BR_NO_ARITH_SHIFT is undefined, or defined to 0, then we _assume_ + * that right-shifting a signed negative integer copies the sign bit + * (arithmetic right-shift). This is "implementation-defined behaviour", + * i.e. it is not undefined, but it may differ between compilers. Each + * compiler is supposed to document its behaviour in that respect. GCC + * explicitly defines that an arithmetic right shift is used. We expect + * all other compilers to do the same, because underlying CPU offer an + * arithmetic right shift opcode that could not be used otherwise. + */ +#define MG_BR_NO_ARITH_SHIFT 0 +#if MG_BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#define ARSHW(x, n) (((uint64_t)(x) >> (n)) \ + | ((-((uint64_t)(x) >> 63)) << (64 - (n)))) +#else +#define ARSH(x, n) ((uint32_t)((*(int32_t *)&(x)) >> (n))) +#define ARSHW(x, n) ((uint64_t)((*(int64_t *)&(x)) >> (n))) +#endif + +/* + * Convert an integer from unsigned big-endian encoding to a sequence of + * 30-bit words in little-endian order. The final "partial" word is + * returned. + */ +static uint32_t +be8_to_le30(uint32_t *dst, const unsigned char *src, size_t len) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + uint32_t b; + + b = src[len]; + if (acc_len < 22) { + acc |= b << acc_len; + acc_len += 8; + } else { + *dst ++ = (acc | (b << acc_len)) & 0x3FFFFFFF; + acc = b >> (30 - acc_len); + acc_len -= 22; + } + } + return acc; +} + +/* + * Convert an integer (30-bit words, little-endian) to unsigned + * big-endian encoding. The total encoding length is provided; all + * the destination bytes will be filled. + */ +static void +le30_to_be8(unsigned char *dst, size_t len, const uint32_t *src) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + if (acc_len < 8) { + uint32_t w; + + w = *src ++; + dst[len] = (unsigned char)(acc | (w << acc_len)); + acc = w >> (8 - acc_len); + acc_len += 22; + } else { + dst[len] = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } + } +} + +/* + * Multiply two integers. Source integers are represented as arrays of + * nine 30-bit words, for values up to 2^270-1. Result is encoded over + * 18 words of 30 bits each. + */ +static void +mul9(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + /* + * Maximum intermediate result is no more than + * 10376293531797946367, which fits in 64 bits. Reason: + * + * 10376293531797946367 = 9 * (2^30-1)^2 + 9663676406 + * 10376293531797946367 < 9663676407 * 2^30 + * + * Thus, adding together 9 products of 30-bit integers, with + * a carry of at most 9663676406, yields an integer that fits + * on 64 bits and generates a carry of at most 9663676406. + */ + uint64_t t[17]; + uint64_t cc; + int i; + + t[ 0] = MG_BR_MUL31(a[0], b[0]); + t[ 1] = MG_BR_MUL31(a[0], b[1]) + + MG_BR_MUL31(a[1], b[0]); + t[ 2] = MG_BR_MUL31(a[0], b[2]) + + MG_BR_MUL31(a[1], b[1]) + + MG_BR_MUL31(a[2], b[0]); + t[ 3] = MG_BR_MUL31(a[0], b[3]) + + MG_BR_MUL31(a[1], b[2]) + + MG_BR_MUL31(a[2], b[1]) + + MG_BR_MUL31(a[3], b[0]); + t[ 4] = MG_BR_MUL31(a[0], b[4]) + + MG_BR_MUL31(a[1], b[3]) + + MG_BR_MUL31(a[2], b[2]) + + MG_BR_MUL31(a[3], b[1]) + + MG_BR_MUL31(a[4], b[0]); + t[ 5] = MG_BR_MUL31(a[0], b[5]) + + MG_BR_MUL31(a[1], b[4]) + + MG_BR_MUL31(a[2], b[3]) + + MG_BR_MUL31(a[3], b[2]) + + MG_BR_MUL31(a[4], b[1]) + + MG_BR_MUL31(a[5], b[0]); + t[ 6] = MG_BR_MUL31(a[0], b[6]) + + MG_BR_MUL31(a[1], b[5]) + + MG_BR_MUL31(a[2], b[4]) + + MG_BR_MUL31(a[3], b[3]) + + MG_BR_MUL31(a[4], b[2]) + + MG_BR_MUL31(a[5], b[1]) + + MG_BR_MUL31(a[6], b[0]); + t[ 7] = MG_BR_MUL31(a[0], b[7]) + + MG_BR_MUL31(a[1], b[6]) + + MG_BR_MUL31(a[2], b[5]) + + MG_BR_MUL31(a[3], b[4]) + + MG_BR_MUL31(a[4], b[3]) + + MG_BR_MUL31(a[5], b[2]) + + MG_BR_MUL31(a[6], b[1]) + + MG_BR_MUL31(a[7], b[0]); + t[ 8] = MG_BR_MUL31(a[0], b[8]) + + MG_BR_MUL31(a[1], b[7]) + + MG_BR_MUL31(a[2], b[6]) + + MG_BR_MUL31(a[3], b[5]) + + MG_BR_MUL31(a[4], b[4]) + + MG_BR_MUL31(a[5], b[3]) + + MG_BR_MUL31(a[6], b[2]) + + MG_BR_MUL31(a[7], b[1]) + + MG_BR_MUL31(a[8], b[0]); + t[ 9] = MG_BR_MUL31(a[1], b[8]) + + MG_BR_MUL31(a[2], b[7]) + + MG_BR_MUL31(a[3], b[6]) + + MG_BR_MUL31(a[4], b[5]) + + MG_BR_MUL31(a[5], b[4]) + + MG_BR_MUL31(a[6], b[3]) + + MG_BR_MUL31(a[7], b[2]) + + MG_BR_MUL31(a[8], b[1]); + t[10] = MG_BR_MUL31(a[2], b[8]) + + MG_BR_MUL31(a[3], b[7]) + + MG_BR_MUL31(a[4], b[6]) + + MG_BR_MUL31(a[5], b[5]) + + MG_BR_MUL31(a[6], b[4]) + + MG_BR_MUL31(a[7], b[3]) + + MG_BR_MUL31(a[8], b[2]); + t[11] = MG_BR_MUL31(a[3], b[8]) + + MG_BR_MUL31(a[4], b[7]) + + MG_BR_MUL31(a[5], b[6]) + + MG_BR_MUL31(a[6], b[5]) + + MG_BR_MUL31(a[7], b[4]) + + MG_BR_MUL31(a[8], b[3]); + t[12] = MG_BR_MUL31(a[4], b[8]) + + MG_BR_MUL31(a[5], b[7]) + + MG_BR_MUL31(a[6], b[6]) + + MG_BR_MUL31(a[7], b[5]) + + MG_BR_MUL31(a[8], b[4]); + t[13] = MG_BR_MUL31(a[5], b[8]) + + MG_BR_MUL31(a[6], b[7]) + + MG_BR_MUL31(a[7], b[6]) + + MG_BR_MUL31(a[8], b[5]); + t[14] = MG_BR_MUL31(a[6], b[8]) + + MG_BR_MUL31(a[7], b[7]) + + MG_BR_MUL31(a[8], b[6]); + t[15] = MG_BR_MUL31(a[7], b[8]) + + MG_BR_MUL31(a[8], b[7]); + t[16] = MG_BR_MUL31(a[8], b[8]); + + /* + * Propagate carries. + */ + cc = 0; + for (i = 0; i < 17; i ++) { + uint64_t w; + + w = t[i] + cc; + d[i] = (uint32_t)w & 0x3FFFFFFF; + cc = w >> 30; + } + d[17] = (uint32_t)cc; +} + +/* + * Square a 270-bit integer, represented as an array of nine 30-bit words. + * Result uses 18 words of 30 bits each. + */ +static void +square9(uint32_t *d, const uint32_t *a) +{ + uint64_t t[17]; + uint64_t cc; + int i; + + t[ 0] = MG_BR_MUL31(a[0], a[0]); + t[ 1] = ((MG_BR_MUL31(a[0], a[1])) << 1); + t[ 2] = MG_BR_MUL31(a[1], a[1]) + + ((MG_BR_MUL31(a[0], a[2])) << 1); + t[ 3] = ((MG_BR_MUL31(a[0], a[3]) + + MG_BR_MUL31(a[1], a[2])) << 1); + t[ 4] = MG_BR_MUL31(a[2], a[2]) + + ((MG_BR_MUL31(a[0], a[4]) + + MG_BR_MUL31(a[1], a[3])) << 1); + t[ 5] = ((MG_BR_MUL31(a[0], a[5]) + + MG_BR_MUL31(a[1], a[4]) + + MG_BR_MUL31(a[2], a[3])) << 1); + t[ 6] = MG_BR_MUL31(a[3], a[3]) + + ((MG_BR_MUL31(a[0], a[6]) + + MG_BR_MUL31(a[1], a[5]) + + MG_BR_MUL31(a[2], a[4])) << 1); + t[ 7] = ((MG_BR_MUL31(a[0], a[7]) + + MG_BR_MUL31(a[1], a[6]) + + MG_BR_MUL31(a[2], a[5]) + + MG_BR_MUL31(a[3], a[4])) << 1); + t[ 8] = MG_BR_MUL31(a[4], a[4]) + + ((MG_BR_MUL31(a[0], a[8]) + + MG_BR_MUL31(a[1], a[7]) + + MG_BR_MUL31(a[2], a[6]) + + MG_BR_MUL31(a[3], a[5])) << 1); + t[ 9] = ((MG_BR_MUL31(a[1], a[8]) + + MG_BR_MUL31(a[2], a[7]) + + MG_BR_MUL31(a[3], a[6]) + + MG_BR_MUL31(a[4], a[5])) << 1); + t[10] = MG_BR_MUL31(a[5], a[5]) + + ((MG_BR_MUL31(a[2], a[8]) + + MG_BR_MUL31(a[3], a[7]) + + MG_BR_MUL31(a[4], a[6])) << 1); + t[11] = ((MG_BR_MUL31(a[3], a[8]) + + MG_BR_MUL31(a[4], a[7]) + + MG_BR_MUL31(a[5], a[6])) << 1); + t[12] = MG_BR_MUL31(a[6], a[6]) + + ((MG_BR_MUL31(a[4], a[8]) + + MG_BR_MUL31(a[5], a[7])) << 1); + t[13] = ((MG_BR_MUL31(a[5], a[8]) + + MG_BR_MUL31(a[6], a[7])) << 1); + t[14] = MG_BR_MUL31(a[7], a[7]) + + ((MG_BR_MUL31(a[6], a[8])) << 1); + t[15] = ((MG_BR_MUL31(a[7], a[8])) << 1); + t[16] = MG_BR_MUL31(a[8], a[8]); + + /* + * Propagate carries. + */ + cc = 0; + for (i = 0; i < 17; i ++) { + uint64_t w; + + w = t[i] + cc; + d[i] = (uint32_t)w & 0x3FFFFFFF; + cc = w >> 30; + } + d[17] = (uint32_t)cc; +} + +/* + * Base field modulus for P-256. + */ +static const uint32_t F256[] = { + + 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x0000003F, 0x00000000, + 0x00000000, 0x00001000, 0x3FFFC000, 0x0000FFFF +}; + +/* + * The 'b' curve equation coefficient for P-256. + */ +static const uint32_t P256_B[] = { + + 0x27D2604B, 0x2F38F0F8, 0x053B0F63, 0x0741AC33, 0x1886BC65, + 0x2EF555DA, 0x293E7B3E, 0x0D762A8E, 0x00005AC6 +}; + +/* + * Addition in the field. Source operands shall fit on 257 bits; output + * will be lower than twice the modulus. + */ +static void +add_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t w, cc; + int i; + + cc = 0; + for (i = 0; i < 9; i ++) { + w = a[i] + b[i] + cc; + d[i] = w & 0x3FFFFFFF; + cc = w >> 30; + } + w >>= 16; + d[8] &= 0xFFFF; + d[3] -= w << 6; + d[6] -= w << 12; + d[7] += w << 14; + cc = w; + for (i = 0; i < 9; i ++) { + w = d[i] + cc; + d[i] = w & 0x3FFFFFFF; + cc = ARSH(w, 30); + } +} + +/* + * Subtraction in the field. Source operands shall be smaller than twice + * the modulus; the result will fulfil the same property. + */ +static void +sub_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t w, cc; + int i; + + /* + * We really compute a - b + 2*p to make sure that the result is + * positive. + */ + w = a[0] - b[0] - 0x00002; + d[0] = w & 0x3FFFFFFF; + w = a[1] - b[1] + ARSH(w, 30); + d[1] = w & 0x3FFFFFFF; + w = a[2] - b[2] + ARSH(w, 30); + d[2] = w & 0x3FFFFFFF; + w = a[3] - b[3] + ARSH(w, 30) + 0x00080; + d[3] = w & 0x3FFFFFFF; + w = a[4] - b[4] + ARSH(w, 30); + d[4] = w & 0x3FFFFFFF; + w = a[5] - b[5] + ARSH(w, 30); + d[5] = w & 0x3FFFFFFF; + w = a[6] - b[6] + ARSH(w, 30) + 0x02000; + d[6] = w & 0x3FFFFFFF; + w = a[7] - b[7] + ARSH(w, 30) - 0x08000; + d[7] = w & 0x3FFFFFFF; + w = a[8] - b[8] + ARSH(w, 30) + 0x20000; + d[8] = w & 0xFFFF; + w >>= 16; + d[8] &= 0xFFFF; + d[3] -= w << 6; + d[6] -= w << 12; + d[7] += w << 14; + cc = w; + for (i = 0; i < 9; i ++) { + w = d[i] + cc; + d[i] = w & 0x3FFFFFFF; + cc = ARSH(w, 30); + } +} + +/* + * Compute a multiplication in F256. Source operands shall be less than + * twice the modulus. + */ +static void +mul_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[18]; + uint64_t s[18]; + uint64_t cc, x; + uint32_t z, c; + int i; + + mul9(t, a, b); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + * + * We use 64-bit intermediate words to allow for carries to + * accumulate easily, before performing the final propagation. + */ + for (i = 0; i < 18; i ++) { + s[i] = t[i]; + } + + for (i = 17; i >= 9; i --) { + uint64_t y; + + y = s[i]; + s[i - 1] += ARSHW(y, 2); + s[i - 2] += (y << 28) & 0x3FFFFFFF; + s[i - 2] -= ARSHW(y, 4); + s[i - 3] -= (y << 26) & 0x3FFFFFFF; + s[i - 5] -= ARSHW(y, 10); + s[i - 6] -= (y << 20) & 0x3FFFFFFF; + s[i - 8] += ARSHW(y, 16); + s[i - 9] += (y << 14) & 0x3FFFFFFF; + } + + /* + * Carry propagation must be signed. Moreover, we may have overdone + * it a bit, and obtain a negative result. + * + * The loop above ran 9 times; each time, each word was augmented + * by at most one extra word (in absolute value). Thus, the top + * word must in fine fit in 39 bits, so the carry below will fit + * on 9 bits. + */ + cc = 0; + for (i = 0; i < 9; i ++) { + x = s[i] + cc; + d[i] = (uint32_t)x & 0x3FFFFFFF; + cc = ARSHW(x, 30); + } + + /* + * All nine words fit on 30 bits, but there may be an extra + * carry for a few bits (at most 9), and that carry may be + * negative. Moreover, we want the result to fit on 257 bits. + * The two lines below ensure that the word in d[] has length + * 256 bits, and the (signed) carry (beyond 2^256) is in cc. The + * significant length of cc is less than 24 bits, so we will be + * able to switch to 32-bit operations. + */ + cc = ARSHW(x, 16); + d[8] &= 0xFFFF; + + /* + * One extra round of reduction, for cc*2^256, which means + * adding cc*(2^224-2^192-2^96+1) to a 256-bit (nonnegative) + * value. If cc is negative, then it may happen (rarely, but + * not neglectibly so) that the result would be negative. In + * order to avoid that, if cc is negative, then we add the + * modulus once. Note that if cc is negative, then propagating + * that carry must yield a value lower than the modulus, so + * adding the modulus once will keep the final result under + * twice the modulus. + */ + z = (uint32_t)cc; + d[3] -= z << 6; + d[6] -= (z << 12) & 0x3FFFFFFF; + d[7] -= ARSH(z, 18); + d[7] += (z << 14) & 0x3FFFFFFF; + d[8] += ARSH(z, 16); + c = z >> 31; + d[0] -= c; + d[3] += c << 6; + d[6] += c << 12; + d[7] -= c << 14; + d[8] += c << 16; + for (i = 0; i < 9; i ++) { + uint32_t w; + + w = d[i] + z; + d[i] = w & 0x3FFFFFFF; + z = ARSH(w, 30); + } +} + +/* + * Compute a square in F256. Source operand shall be less than + * twice the modulus. + */ +static void +square_f256(uint32_t *d, const uint32_t *a) +{ + uint32_t t[18]; + uint64_t s[18]; + uint64_t cc, x; + uint32_t z, c; + int i; + + square9(t, a); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + * + * We use 64-bit intermediate words to allow for carries to + * accumulate easily, before performing the final propagation. + */ + for (i = 0; i < 18; i ++) { + s[i] = t[i]; + } + + for (i = 17; i >= 9; i --) { + uint64_t y; + + y = s[i]; + s[i - 1] += ARSHW(y, 2); + s[i - 2] += (y << 28) & 0x3FFFFFFF; + s[i - 2] -= ARSHW(y, 4); + s[i - 3] -= (y << 26) & 0x3FFFFFFF; + s[i - 5] -= ARSHW(y, 10); + s[i - 6] -= (y << 20) & 0x3FFFFFFF; + s[i - 8] += ARSHW(y, 16); + s[i - 9] += (y << 14) & 0x3FFFFFFF; + } + + /* + * Carry propagation must be signed. Moreover, we may have overdone + * it a bit, and obtain a negative result. + * + * The loop above ran 9 times; each time, each word was augmented + * by at most one extra word (in absolute value). Thus, the top + * word must in fine fit in 39 bits, so the carry below will fit + * on 9 bits. + */ + cc = 0; + for (i = 0; i < 9; i ++) { + x = s[i] + cc; + d[i] = (uint32_t)x & 0x3FFFFFFF; + cc = ARSHW(x, 30); + } + + /* + * All nine words fit on 30 bits, but there may be an extra + * carry for a few bits (at most 9), and that carry may be + * negative. Moreover, we want the result to fit on 257 bits. + * The two lines below ensure that the word in d[] has length + * 256 bits, and the (signed) carry (beyond 2^256) is in cc. The + * significant length of cc is less than 24 bits, so we will be + * able to switch to 32-bit operations. + */ + cc = ARSHW(x, 16); + d[8] &= 0xFFFF; + + /* + * One extra round of reduction, for cc*2^256, which means + * adding cc*(2^224-2^192-2^96+1) to a 256-bit (nonnegative) + * value. If cc is negative, then it may happen (rarely, but + * not neglectibly so) that the result would be negative. In + * order to avoid that, if cc is negative, then we add the + * modulus once. Note that if cc is negative, then propagating + * that carry must yield a value lower than the modulus, so + * adding the modulus once will keep the final result under + * twice the modulus. + */ + z = (uint32_t)cc; + d[3] -= z << 6; + d[6] -= (z << 12) & 0x3FFFFFFF; + d[7] -= ARSH(z, 18); + d[7] += (z << 14) & 0x3FFFFFFF; + d[8] += ARSH(z, 16); + c = z >> 31; + d[0] -= c; + d[3] += c << 6; + d[6] += c << 12; + d[7] -= c << 14; + d[8] += c << 16; + for (i = 0; i < 9; i ++) { + uint32_t w; + + w = d[i] + z; + d[i] = w & 0x3FFFFFFF; + z = ARSH(w, 30); + } +} + +/* + * Perform a "final reduction" in field F256 (field for curve P-256). + * The source value must be less than twice the modulus. If the value + * is not lower than the modulus, then the modulus is subtracted and + * this function returns 1; otherwise, it leaves it untouched and it + * returns 0. + */ +static uint32_t +reduce_final_f256(uint32_t *d) +{ + uint32_t t[9]; + uint32_t cc; + int i; + + cc = 0; + for (i = 0; i < 9; i ++) { + uint32_t w; + + w = d[i] - F256[i] - cc; + cc = w >> 31; + t[i] = w & 0x3FFFFFFF; + } + cc ^= 1; + MG_BR_CCOPY(cc, d, t, sizeof t); + return cc; +} + +/* + * Jacobian coordinates for a point in P-256: affine coordinates (X,Y) + * are such that: + * X = x / z^2 + * Y = y / z^3 + * For the point at infinity, z = 0. + * Each point thus admits many possible representations. + * + * Coordinates are represented in arrays of 32-bit integers, each holding + * 30 bits of data. Values may also be slightly greater than the modulus, + * but they will always be lower than twice the modulus. + */ +struct p256_jacobian { + uint32_t x[9]; + uint32_t y[9]; + uint32_t z[9]; +}; + +/* + * Convert a point to affine coordinates: + * - If the point is the point at infinity, then all three coordinates + * are set to 0. + * - Otherwise, the 'z' coordinate is set to 1, and the 'x' and 'y' + * coordinates are the 'X' and 'Y' affine coordinates. + * The coordinates are guaranteed to be lower than the modulus. + */ +static void +p256_to_affine(struct p256_jacobian *P) +{ + uint32_t t1[9], t2[9]; + int i; + + /* + * Invert z with a modular exponentiation: the modulus is + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1, and the exponent is + * p-2. Exponent bit pattern (from high to low) is: + * - 32 bits of value 1 + * - 31 bits of value 0 + * - 1 bit of value 1 + * - 96 bits of value 0 + * - 94 bits of value 1 + * - 1 bit of value 0 + * - 1 bit of value 1 + * Thus, we precompute z^(2^31-1) to speed things up. + * + * If z = 0 (point at infinity) then the modular exponentiation + * will yield 0, which leads to the expected result (all three + * coordinates set to 0). + */ + + /* + * A simple square-and-multiply for z^(2^31-1). We could save about + * two dozen multiplications here with an addition chain, but + * this would require a bit more code, and extra stack buffers. + */ + memcpy(t1, P->z, sizeof P->z); + for (i = 0; i < 30; i ++) { + square_f256(t1, t1); + mul_f256(t1, t1, P->z); + } + + /* + * Square-and-multiply. Apart from the squarings, we have a few + * multiplications to set bits to 1; we multiply by the original z + * for setting 1 bit, and by t1 for setting 31 bits. + */ + memcpy(t2, P->z, sizeof P->z); + for (i = 1; i < 256; i ++) { + square_f256(t2, t2); + switch (i) { + case 31: + case 190: + case 221: + case 252: + mul_f256(t2, t2, t1); + break; + case 63: + case 253: + case 255: + mul_f256(t2, t2, P->z); + break; + } + } + + /* + * Now that we have 1/z, multiply x by 1/z^2 and y by 1/z^3. + */ + mul_f256(t1, t2, t2); + mul_f256(P->x, t1, P->x); + mul_f256(t1, t1, t2); + mul_f256(P->y, t1, P->y); + reduce_final_f256(P->x); + reduce_final_f256(P->y); + + /* + * Multiply z by 1/z. If z = 0, then this will yield 0, otherwise + * this will set z to 1. + */ + mul_f256(P->z, P->z, t2); + reduce_final_f256(P->z); +} + +/* + * Double a point in P-256. This function works for all valid points, + * including the point at infinity. + */ +static void +p256_double(struct p256_jacobian *Q) +{ + /* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * These formulas work for all points, including points of order 2 + * and points at infinity: + * - If y = 0 then z' = 0. But there is no such point in P-256 + * anyway. + * - If z = 0 then z' = 0. + */ + uint32_t t1[9], t2[9], t3[9], t4[9]; + + /* + * Compute z^2 in t1. + */ + square_f256(t1, Q->z); + + /* + * Compute x-z^2 in t2 and x+z^2 in t1. + */ + add_f256(t2, Q->x, t1); + sub_f256(t1, Q->x, t1); + + /* + * Compute 3*(x+z^2)*(x-z^2) in t1. + */ + mul_f256(t3, t1, t2); + add_f256(t1, t3, t3); + add_f256(t1, t3, t1); + + /* + * Compute 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + square_f256(t3, Q->y); + add_f256(t3, t3, t3); + mul_f256(t2, Q->x, t3); + add_f256(t2, t2, t2); + + /* + * Compute x' = m^2 - 2*s. + */ + square_f256(Q->x, t1); + sub_f256(Q->x, Q->x, t2); + sub_f256(Q->x, Q->x, t2); + + /* + * Compute z' = 2*y*z. + */ + mul_f256(t4, Q->y, Q->z); + add_f256(Q->z, t4, t4); + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + sub_f256(t2, t2, Q->x); + mul_f256(Q->y, t1, t2); + square_f256(t4, t3); + add_f256(t4, t4, t4); + sub_f256(Q->y, Q->y, t4); +} + +/* + * Add point P2 to point P1. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 but P2 != 0 + * - If P1 != 0 but P2 == 0 + * - If P1 == P2 + * + * In all three cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - P1 == 0 and P2 == 0 + * - The Y coordinate of one of the points is 0 and the other point is + * the point at infinity. + * + * The third case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 and P2 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add(struct p256_jacobian *P1, const struct p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + */ + uint32_t t1[9], t2[9], t3[9], t4[9], t5[9], t6[9], t7[9]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + square_f256(t3, P2->z); + mul_f256(t1, P1->x, t3); + mul_f256(t4, P2->z, t3); + mul_f256(t3, P1->y, t4); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + sub_f256(t2, t2, t1); + sub_f256(t4, t4, t3); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 9; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + sub_f256(P1->x, P1->x, t5); + sub_f256(P1->x, P1->x, t6); + sub_f256(P1->x, P1->x, t6); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + sub_f256(t6, t6, P1->x); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + sub_f256(P1->y, P1->y, t1); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(t1, P1->z, P2->z); + mul_f256(P1->z, t1, t2); + + return ret; +} + +/* + * Add point P2 to point P1. This is a specialised function for the + * case when P2 is a non-zero point in affine coordinate. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 + * - If P1 == P2 + * + * In both cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - The Y coordinate of P2 is 0 and P1 is the point at infinity. + * + * The second case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add_mixed(struct p256_jacobian *P1, const struct p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 + * u2 = x2 * z1^2 + * s1 = y1 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 + */ + uint32_t t1[9], t2[9], t3[9], t4[9], t5[9], t6[9], t7[9]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1 (in t1) and s1 = y1 (in t3). + */ + memcpy(t1, P1->x, sizeof t1); + memcpy(t3, P1->y, sizeof t3); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + sub_f256(t2, t2, t1); + sub_f256(t4, t4, t3); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 9; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + sub_f256(P1->x, P1->x, t5); + sub_f256(P1->x, P1->x, t6); + sub_f256(P1->x, P1->x, t6); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + sub_f256(t6, t6, P1->x); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + sub_f256(P1->y, P1->y, t1); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(P1->z, P1->z, t2); + + return ret; +} + +/* + * Decode a P-256 point. This function does not support the point at + * infinity. Returned value is 0 if the point is invalid, 1 otherwise. + */ +static uint32_t +p256_decode(struct p256_jacobian *P, const void *src, size_t len) +{ + const unsigned char *buf; + uint32_t tx[9], ty[9], t1[9], t2[9]; + uint32_t bad; + int i; + + if (len != 65) { + return 0; + } + buf = (const unsigned char *)src; + + /* + * First byte must be 0x04 (uncompressed format). We could support + * "hybrid format" (first byte is 0x06 or 0x07, and encodes the + * least significant bit of the Y coordinate), but it is explicitly + * forbidden by RFC 5480 (section 2.2). + */ + bad = MG_BR_NEQ(buf[0], 0x04); + + /* + * Decode the coordinates, and check that they are both lower + * than the modulus. + */ + tx[8] = be8_to_le30(tx, buf + 1, 32); + ty[8] = be8_to_le30(ty, buf + 33, 32); + bad |= reduce_final_f256(tx); + bad |= reduce_final_f256(ty); + + /* + * Check curve equation. + */ + square_f256(t1, tx); + mul_f256(t1, tx, t1); + square_f256(t2, ty); + sub_f256(t1, t1, tx); + sub_f256(t1, t1, tx); + sub_f256(t1, t1, tx); + add_f256(t1, t1, P256_B); + sub_f256(t1, t1, t2); + reduce_final_f256(t1); + for (i = 0; i < 9; i ++) { + bad |= t1[i]; + } + + /* + * Copy coordinates to the point structure. + */ + memcpy(P->x, tx, sizeof tx); + memcpy(P->y, ty, sizeof ty); + memset(P->z, 0, sizeof P->z); + P->z[0] = 1; + return MG_BR_EQ(bad, 0); +} + +/* + * Encode a point into a buffer. This function assumes that the point is + * valid, in affine coordinates, and not the point at infinity. + */ +static void +p256_encode(void *dst, const struct p256_jacobian *P) +{ + unsigned char *buf; + + buf = (unsigned char *)dst; + buf[0] = 0x04; + le30_to_be8(buf + 1, 32, P->x); + le30_to_be8(buf + 33, 32, P->y); +} + +/* + * Multiply a curve point by an integer. The integer is assumed to be + * lower than the curve order, and the base point must not be the point + * at infinity. + */ +static void +p256_mul(struct p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 2-bit window to handle multiplier bits by pairs. + * The precomputed window really is the points P2 and P3. + */ + uint32_t qz; + struct p256_jacobian P2, P3, Q, T, U; + + /* + * Compute window values. + */ + P2 = *P; + p256_double(&P2); + P3 = *P; + p256_add(&P3, &P2); + + /* + * We start with Q = 0. We process multiplier bits 2 by 2. + */ + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + p256_double(&Q); + p256_double(&Q); + T = *P; + U = Q; + bits = (*x >> k) & (uint32_t)3; + bnz = MG_BR_NEQ(bits, 0); + MG_BR_CCOPY(MG_BR_EQ(bits, 2), &T, &P2, sizeof T); + MG_BR_CCOPY(MG_BR_EQ(bits, 3), &T, &P3, sizeof T); + p256_add(&U, &T); + MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q); + MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + *P = Q; +} + +/* + * Precomputed window: k*G points, where G is the curve generator, and k + * is an integer from 1 to 15 (inclusive). The X and Y coordinates of + * the point are encoded as 9 words of 30 bits each (little-endian + * order). + */ +static const uint32_t Gwin[15][18] = { + + { 0x1898C296, 0x1284E517, 0x1EB33A0F, 0x00DF604B, + 0x2440F277, 0x339B958E, 0x04247F8B, 0x347CB84B, + 0x00006B17, 0x37BF51F5, 0x2ED901A0, 0x3315ECEC, + 0x338CD5DA, 0x0F9E162B, 0x1FAD29F0, 0x27F9B8EE, + 0x10B8BF86, 0x00004FE3 }, + + { 0x07669978, 0x182D23F1, 0x3F21B35A, 0x225A789D, + 0x351AC3C0, 0x08E00C12, 0x34F7E8A5, 0x1EC62340, + 0x00007CF2, 0x227873D1, 0x3812DE74, 0x0E982299, + 0x1F6B798F, 0x3430DBBA, 0x366B1A7D, 0x2D040293, + 0x154436E3, 0x00000777 }, + + { 0x06E7FD6C, 0x2D05986F, 0x3ADA985F, 0x31ADC87B, + 0x0BF165E6, 0x1FBE5475, 0x30A44C8F, 0x3934698C, + 0x00005ECB, 0x227D5032, 0x29E6C49E, 0x04FB83D9, + 0x0AAC0D8E, 0x24A2ECD8, 0x2C1B3869, 0x0FF7E374, + 0x19031266, 0x00008734 }, + + { 0x2B030852, 0x024C0911, 0x05596EF5, 0x07F8B6DE, + 0x262BD003, 0x3779967B, 0x08FBBA02, 0x128D4CB4, + 0x0000E253, 0x184ED8C6, 0x310B08FC, 0x30EE0055, + 0x3F25B0FC, 0x062D764E, 0x3FB97F6A, 0x33CC719D, + 0x15D69318, 0x0000E0F1 }, + + { 0x03D033ED, 0x05552837, 0x35BE5242, 0x2320BF47, + 0x268FDFEF, 0x13215821, 0x140D2D78, 0x02DE9454, + 0x00005159, 0x3DA16DA4, 0x0742ED13, 0x0D80888D, + 0x004BC035, 0x0A79260D, 0x06FCDAFE, 0x2727D8AE, + 0x1F6A2412, 0x0000E0C1 }, + + { 0x3C2291A9, 0x1AC2ABA4, 0x3B215B4C, 0x131D037A, + 0x17DDE302, 0x0C90B2E2, 0x0602C92D, 0x05CA9DA9, + 0x0000B01A, 0x0FC77FE2, 0x35F1214E, 0x07E16BDF, + 0x003DDC07, 0x2703791C, 0x3038B7EE, 0x3DAD56FE, + 0x041D0C8D, 0x0000E85C }, + + { 0x3187B2A3, 0x0018A1C0, 0x00FEF5B3, 0x3E7E2E2A, + 0x01FB607E, 0x2CC199F0, 0x37B4625B, 0x0EDBE82F, + 0x00008E53, 0x01F400B4, 0x15786A1B, 0x3041B21C, + 0x31CD8CF2, 0x35900053, 0x1A7E0E9B, 0x318366D0, + 0x076F780C, 0x000073EB }, + + { 0x1B6FB393, 0x13767707, 0x3CE97DBB, 0x348E2603, + 0x354CADC1, 0x09D0B4EA, 0x1B053404, 0x1DE76FBA, + 0x000062D9, 0x0F09957E, 0x295029A8, 0x3E76A78D, + 0x3B547DAE, 0x27CEE0A2, 0x0575DC45, 0x1D8244FF, + 0x332F647A, 0x0000AD5A }, + + { 0x10949EE0, 0x1E7A292E, 0x06DF8B3D, 0x02B2E30B, + 0x31F8729E, 0x24E35475, 0x30B71878, 0x35EDBFB7, + 0x0000EA68, 0x0DD048FA, 0x21688929, 0x0DE823FE, + 0x1C53FAA9, 0x0EA0C84D, 0x052A592A, 0x1FCE7870, + 0x11325CB2, 0x00002A27 }, + + { 0x04C5723F, 0x30D81A50, 0x048306E4, 0x329B11C7, + 0x223FB545, 0x085347A8, 0x2993E591, 0x1B5ACA8E, + 0x0000CEF6, 0x04AF0773, 0x28D2EEA9, 0x2751EEEC, + 0x037B4A7F, 0x3B4C1059, 0x08F37674, 0x2AE906E1, + 0x18A88A6A, 0x00008786 }, + + { 0x34BC21D1, 0x0CCE474D, 0x15048BF4, 0x1D0BB409, + 0x021CDA16, 0x20DE76C3, 0x34C59063, 0x04EDE20E, + 0x00003ED1, 0x282A3740, 0x0BE3BBF3, 0x29889DAE, + 0x03413697, 0x34C68A09, 0x210EBE93, 0x0C8A224C, + 0x0826B331, 0x00009099 }, + + { 0x0624E3C4, 0x140317BA, 0x2F82C99D, 0x260C0A2C, + 0x25D55179, 0x194DCC83, 0x3D95E462, 0x356F6A05, + 0x0000741D, 0x0D4481D3, 0x2657FC8B, 0x1BA5CA71, + 0x3AE44B0D, 0x07B1548E, 0x0E0D5522, 0x05FDC567, + 0x2D1AA70E, 0x00000770 }, + + { 0x06072C01, 0x23857675, 0x1EAD58A9, 0x0B8A12D9, + 0x1EE2FC79, 0x0177CB61, 0x0495A618, 0x20DEB82B, + 0x0000177C, 0x2FC7BFD8, 0x310EEF8B, 0x1FB4DF39, + 0x3B8530E8, 0x0F4E7226, 0x0246B6D0, 0x2A558A24, + 0x163353AF, 0x000063BB }, + + { 0x24D2920B, 0x1C249DCC, 0x2069C5E5, 0x09AB2F9E, + 0x36DF3CF1, 0x1991FD0C, 0x062B97A7, 0x1E80070E, + 0x000054E7, 0x20D0B375, 0x2E9F20BD, 0x35090081, + 0x1C7A9DDC, 0x22E7C371, 0x087E3016, 0x03175421, + 0x3C6ECA7D, 0x0000F599 }, + + { 0x259B9D5F, 0x0D9A318F, 0x23A0EF16, 0x00EBE4B7, + 0x088265AE, 0x2CDE2666, 0x2BAE7ADF, 0x1371A5C6, + 0x0000F045, 0x0D034F36, 0x1F967378, 0x1B5FA3F4, + 0x0EC8739D, 0x1643E62A, 0x1653947E, 0x22D1F4E6, + 0x0FB8D64B, 0x0000B5B9 } +}; + +/* + * Lookup one of the Gwin[] values, by index. This is constant-time. + */ +static void +lookup_Gwin(struct p256_jacobian *T, uint32_t idx) +{ + uint32_t xy[18]; + uint32_t k; + size_t u; + + memset(xy, 0, sizeof xy); + for (k = 0; k < 15; k ++) { + uint32_t m; + + m = -MG_BR_EQ(idx, k + 1); + for (u = 0; u < 18; u ++) { + xy[u] |= m & Gwin[k][u]; + } + } + memcpy(T->x, &xy[0], sizeof T->x); + memcpy(T->y, &xy[9], sizeof T->y); + memset(T->z, 0, sizeof T->z); + T->z[0] = 1; +} + +/* + * Multiply the generator by an integer. The integer is assumed non-zero + * and lower than the curve order. + */ +static void +p256_mulgen(struct p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 4-bit window to handle multiplier bits by groups + * of 4. The precomputed window is constant static data, with + * points in affine coordinates; we use a constant-time lookup. + */ + struct p256_jacobian Q; + uint32_t qz; + + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + unsigned bx; + + bx = *x ++; + for (k = 0; k < 2; k ++) { + uint32_t bits; + uint32_t bnz; + struct p256_jacobian T, U; + + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + bits = (bx >> 4) & 0x0F; + bnz = MG_BR_NEQ(bits, 0); + lookup_Gwin(&T, bits); + U = Q; + p256_add_mixed(&U, &T); + MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q); + MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + bx <<= 4; + } + } + *P = Q; +} + +static const unsigned char P256_G[] = { + 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, + 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, + 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, + 0x98, 0xC2, 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, + 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, + 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, + 0x68, 0x37, 0xBF, 0x51, 0xF5 +}; + +static const unsigned char P256_N[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, + 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, + 0x25, 0x51 +}; + +static const unsigned char * +p256_api_generator(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P256_G; + return P256_G; +} + +static const unsigned char * +p256_api_order(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P256_N; + return P256_N; +} + +static size_t +p256_api_xoff(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return 1; +} + +static uint32_t +p256_api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + struct p256_jacobian P; + + (void)curve; + if (Glen != 65) { + return 0; + } + r = p256_decode(&P, G, Glen); + p256_mul(&P, x, xlen); + p256_to_affine(&P); + p256_encode(G, &P); + return r; +} + +static size_t +p256_api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + struct p256_jacobian P; + + (void)curve; + p256_mulgen(&P, x, xlen); + p256_to_affine(&P); + p256_encode(R, &P); + return 65; +} + +static uint32_t +p256_api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + struct p256_jacobian P, Q; + uint32_t r, t, z; + int i; + + (void)curve; + if (len != 65) { + return 0; + } + r = p256_decode(&P, A, len); + p256_mul(&P, x, xlen); + if (B == NULL) { + p256_mulgen(&Q, y, ylen); + } else { + r &= p256_decode(&Q, B, len); + p256_mul(&Q, y, ylen); + } + + /* + * The final addition may fail in case both points are equal. + */ + t = p256_add(&P, &Q); + reduce_final_f256(P.z); + z = 0; + for (i = 0; i < 9; i ++) { + z |= P.z[i]; + } + z = MG_BR_EQ(z, 0); + p256_double(&Q); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + MG_BR_CCOPY(z & ~t, &P, &Q, sizeof Q); + p256_to_affine(&P); + p256_encode(A, &P); + r &= ~(z & t); + return r; +} + +static const struct mg_br_ec_impl mg_br_ec_p256_m31 = { + (uint32_t)0x00800000, + &p256_api_generator, + &p256_api_order, + &p256_api_xoff, + &p256_api_mul, + &p256_api_mulgen, + &p256_api_muladd +}; + +#undef ARSH +#undef ARSHW + +// mg_p256_*() for MG_P256_BEAR, see tls_p256.h +int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]) { + const unsigned char *n; + unsigned char q[65]; + uint32_t x[I31_LEN], nn[I31_LEN]; + size_t nlen; + int tries, ok = 0; + n = mg_br_ec_p256_m31.order(MG_BR_EC_secp256r1, &nlen); + mg_br_i31_decode(nn, n, nlen); + for (tries = 0; tries < 64 && !ok; tries++) { // Private key in [1, n-1] + if (!mg_random(private_key, 32)) break; + ok = mg_br_i31_decode_mod(x, private_key, 32, nn) && !mg_br_i31_iszero(x); + } + if (ok) { + mg_br_ec_p256_m31.mulgen(q, private_key, 32, MG_BR_EC_secp256r1); + memcpy(public_key, q + 1, 64); + } + mg_bzero((unsigned char *) x, sizeof(x)); + return ok; +} + +int mg_p256_compute_public_key(const uint8_t private_key[32], + uint8_t public_key[64]) { + const unsigned char *n; + unsigned char q[65]; + uint32_t x[I31_LEN], nn[I31_LEN]; + size_t nlen; + int ok; + n = mg_br_ec_p256_m31.order(MG_BR_EC_secp256r1, &nlen); + mg_br_i31_decode(nn, n, nlen); + ok = mg_br_i31_decode_mod(x, private_key, 32, nn) && !mg_br_i31_iszero(x); + if (ok) { + mg_br_ec_p256_m31.mulgen(q, private_key, 32, MG_BR_EC_secp256r1); + memcpy(public_key, q + 1, 64); + } + mg_bzero((unsigned char *) x, sizeof(x)); + return ok; +} + +int mg_p256_shared_secret(const uint8_t public_key[64], + const uint8_t private_key[32], uint8_t secret[32]) { + unsigned char q[65]; + uint32_t ok; + q[0] = 0x04; + memcpy(q + 1, public_key, 64); + ok = mg_br_ec_p256_m31.mul(q, sizeof(q), private_key, 32, + MG_BR_EC_secp256r1); // Validates the point + memcpy(secret, q + 1, 32); + mg_bzero(q, sizeof(q)); + return ok == 1; +} + +int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, const uint8_t k[32], + uint8_t signature[64]) { + return mg_br_ecdsa_i31_sign_raw(&mg_br_ec_p256_m31, MG_BR_EC_secp256r1, + hash, hash_size, private_key, 32, k, + signature) == 64; +} + +int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash, + size_t hash_size, const uint8_t signature[64]) { + unsigned char q[65]; + q[0] = 0x04; + memcpy(q + 1, public_key, 64); + return mg_br_ecdsa_i31_vrfy_raw(&mg_br_ec_p256_m31, MG_BR_EC_secp256r1, hash, + hash_size, q, sizeof(q), signature, 64) == 1; +} +#endif // MG_P256 == MG_P256_BEAR + +// P-256 group order n, big endian +static const uint8_t s_p256_n[32] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, + 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51}; + +// Compute d = a - n, return 1 if a < n (borrow). Constant time +static uint32_t p256_sub_n(uint8_t d[32], const uint8_t a[32]) { + uint32_t borrow = 0, v; + int i; + for (i = 31; i >= 0; i--) { + v = (uint32_t) a[i] - s_p256_n[i] - borrow; + d[i] = (uint8_t) v; + borrow = (v >> 8) & 1; + } + return borrow; +} + +// ECDSA with deterministic k, RFC 6979 section 3.2, HMAC-SHA256. For P-256, +// qlen == hlen == 256 bits, so bits2int() is just the leftmost 32 bytes +int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, uint8_t signature[64]) { + uint8_t v[32], k[32], buf[32 + 1 + 32 + 32], h1[32], t[32]; + uint32_t lt, nz; + int i, tries, ok = 0; + + // h1 = bits2octets(hash): leftmost 256 bits, reduced modulo n + memset(h1, 0, sizeof(h1)); + if (hash_size >= 32) { + memcpy(h1, hash, 32); + } else { + memcpy(h1 + 32 - hash_size, hash, hash_size); + } + lt = p256_sub_n(t, h1); + for (i = 0; i < 32; i++) h1[i] = (uint8_t) MG_BR_MUX(lt, h1[i], t[i]); + + // Steps b-g: V = 0x01..., K = 0x00..., two HMAC rounds with x and h1 + memset(v, 1, sizeof(v)); + memset(k, 0, sizeof(k)); + memcpy(buf + 33, private_key, 32); + memcpy(buf + 65, h1, 32); + for (i = 0; i < 2; i++) { + memcpy(buf, v, 32); + buf[32] = (uint8_t) i; + mg_hmac_sha256(k, k, 32, buf, sizeof(buf)); + mg_hmac_sha256(v, k, 32, v, 32); + } + + // Step h: candidate k = V, must be in [1, n-1] + for (tries = 0; tries < 64 && !ok; tries++) { + mg_hmac_sha256(v, k, 32, v, 32); + for (nz = 0, i = 0; i < 32; i++) nz |= v[i]; + lt = p256_sub_n(t, v); + if (nz != 0 && lt) { + ok = mg_p256_sign_with_k(private_key, hash, hash_size, v, signature); + } + if (!ok) { // K = HMAC_K(V || 0x00), V = HMAC_K(V) + memcpy(buf, v, 32); + buf[32] = 0; + mg_hmac_sha256(k, k, 32, buf, 33); + mg_hmac_sha256(v, k, 32, v, 32); + } + } + mg_bzero(v, sizeof(v)); + mg_bzero(k, sizeof(k)); + mg_bzero(buf, sizeof(buf)); + mg_bzero(t, sizeof(t)); + return ok; +} + +#undef I31_LEN +#undef POINT_LEN +#undef ORDER_LEN +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) diff --git a/src/tls_p256.h b/src/tls_p256.h new file mode 100644 index 00000000..306f9415 --- /dev/null +++ b/src/tls_p256.h @@ -0,0 +1,181 @@ +// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by +// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN) +// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA +// code is always compiled, it is also used by P-384, see tls_p384.c +// +// Copyright (c) 2016 Thomas Pornin +// Copyright (c) 2017 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the +// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not +// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is +// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and +// public key directly +#pragma once + +#include "arch.h" +#include "config.h" +#include "str.h" +#include "tls.h" + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + +#define MG_BR_EC_secp256r1 23 // Curve IDs, same as TLS named groups +#define MG_BR_EC_secp384r1 24 +#define MG_BR_MAX_EC_SIZE 384 // Largest supported curve, bits + +// From BearSSL inc/bearssl_ec.h: an EC implementation +struct mg_br_ec_impl { + uint32_t supported_curves; // Bit mask of curve IDs + const unsigned char *(*generator)(int curve, size_t *len); + const unsigned char *(*order)(int curve, size_t *len); + size_t (*xoff)(int curve, size_t *len); + uint32_t (*mul)(unsigned char *G, size_t Glen, const unsigned char *x, + size_t xlen, int curve); + size_t (*mulgen)(unsigned char *R, const unsigned char *x, size_t xlen, + int curve); + uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve); +}; + +// From BearSSL src/inner.h: constant-time helpers +static inline uint32_t MG_BR_NOT(uint32_t ctl) { + return ctl ^ 1; +} +static inline uint32_t MG_BR_MUX(uint32_t ctl, uint32_t x, uint32_t y) { + return y ^ (-ctl & (x ^ y)); +} +static inline uint32_t MG_BR_EQ(uint32_t x, uint32_t y) { + uint32_t q = x ^ y; + return MG_BR_NOT((q | -q) >> 31); +} +static inline uint32_t MG_BR_NEQ(uint32_t x, uint32_t y) { + uint32_t q = x ^ y; + return (q | -q) >> 31; +} +static inline uint32_t MG_BR_GT(uint32_t x, uint32_t y) { + uint32_t z = y - x; + return (z ^ ((x ^ y) & (x ^ z))) >> 31; +} +#define MG_BR_GE(x, y) MG_BR_NOT(MG_BR_GT(y, x)) +#define MG_BR_LT(x, y) MG_BR_GT(y, x) +static inline int32_t MG_BR_CMP(uint32_t x, uint32_t y) { + return (int32_t) MG_BR_GT(x, y) | -(int32_t) MG_BR_GT(y, x); +} +static inline uint32_t MG_BR_BIT_LENGTH(uint32_t x) { + uint32_t k, c; + k = MG_BR_NEQ(x, 0); + c = MG_BR_GT(x, 0xFFFF), x = MG_BR_MUX(c, x >> 16, x), k += c << 4; + c = MG_BR_GT(x, 0x00FF), x = MG_BR_MUX(c, x >> 8, x), k += c << 3; + c = MG_BR_GT(x, 0x000F), x = MG_BR_MUX(c, x >> 4, x), k += c << 2; + c = MG_BR_GT(x, 0x0003), x = MG_BR_MUX(c, x >> 2, x), k += c << 1; + k += MG_BR_GT(x, 0x0001); + return k; +} +#define MG_BR_MUL31(x, y) ((uint64_t) (x) * (uint64_t) (y)) +#define MG_BR_MUL31_lo(x, y) \ + (((uint32_t) (x) * (uint32_t) (y)) & (uint32_t) 0x7FFFFFFF) +#define MG_BR_CCOPY mg_br_ccopy +void mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len); +uint32_t mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r); +static inline uint32_t mg_br_rem(uint32_t hi, uint32_t lo, uint32_t d) { + uint32_t r; + mg_br_divrem(hi, lo, d, &r); + return r; +} +static inline uint32_t mg_br_div(uint32_t hi, uint32_t lo, uint32_t d) { + uint32_t r; + return mg_br_divrem(hi, lo, d, &r); +} +static inline void mg_br_enc32be(void *dst, uint32_t x) { + unsigned char *buf = (unsigned char *) dst; + buf[0] = (unsigned char) (x >> 24), buf[1] = (unsigned char) (x >> 16); + buf[2] = (unsigned char) (x >> 8), buf[3] = (unsigned char) x; +} +static inline void mg_br_i31_zero(uint32_t *x, uint32_t bit_len) { + *x++ = bit_len; + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x); +} + +// Big integers, "i31" format, see BearSSL src/inner.h +uint32_t mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl); +uint32_t mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl); +uint32_t mg_br_i31_bit_length(uint32_t *x, size_t xlen); +uint32_t mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len, + const uint32_t *m); +void mg_br_i31_decode(uint32_t *x, const void *src, size_t len); +void mg_br_i31_encode(void *dst, size_t len, const uint32_t *x); +void mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i); +void mg_br_i31_to_monty(uint32_t *x, const uint32_t *m); +uint32_t mg_br_i31_iszero(const uint32_t *x); +void mg_br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, + uint32_t *t2); +void mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i); +void mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m); +uint32_t mg_br_i31_ninv31(uint32_t x); +void mg_br_i31_rshift(uint32_t *x, int count); + +// ECDSA, see BearSSL src/ec/ecdsa_i31_*.c. q is an uncompressed point +void mg_br_ecdsa_i31_bits2int(uint32_t *x, const void *src, size_t len, + uint32_t ebitlen); +uint32_t mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash, size_t hash_len, + const unsigned char *q, size_t qlen, + const void *sig, size_t sig_len); +size_t mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve, + const void *hash, size_t hash_len, + const unsigned char *x, size_t xlen, + const unsigned char *k, void *sig); + +// P-256 (secp256r1) API. Keys and signatures are big endian byte strings, +// a public key is X || Y, a signature is r || s. Functions return 1 on +// success, 0 on failure + +// Generate a random key pair, using mg_random() +int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]); + +// Compute public key from a private key. Return 0 if private key is invalid +int mg_p256_compute_public_key(const uint8_t private_key[32], + uint8_t public_key[64]); + +// ECDH. Validates the peer public key, returns 0 if it is not on the curve +int mg_p256_shared_secret(const uint8_t public_key[64], + const uint8_t private_key[32], uint8_t secret[32]); + +// ECDSA sign with a deterministic k, RFC 6979 with HMAC-SHA256 +int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, uint8_t signature[64]); + +// ECDSA verify. Validates the public key +int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash, + size_t hash_size, const uint8_t signature[64]); + +// Internal: ECDSA sign with a given k, 32 bytes big endian, in [1, n-1]. +// Provided by the selected implementation, used by mg_p256_sign() +int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, const uint8_t k[32], + uint8_t signature[64]); +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) diff --git a/src/tls_p256_cortex.c b/src/tls_p256_cortex.c new file mode 100644 index 00000000..cc778f5e --- /dev/null +++ b/src/tls_p256_cortex.c @@ -0,0 +1,2261 @@ +// P-256 for Cortex-M4/M7/M33, https://github.com/Emill/P256-Cortex-M4 +// commit ecd3ec2 +// Enabled by MG_P256=MG_P256_LENNGREN. Provides mg_p256_*(), see tls_p256.h +// +// Copyright (c) 2017-2021 Emil Lenngren +// Copyright (c) 2021 Shortcut Labs AB +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: the library's C code, header, config and GCC assembly are +// merged into one file, the assembly is a top-level __asm__ without .end. +// Trimmed with unifdef to keygen, ECDH, sign and verify. Uses the no-FPU field +// multiplication and the constant RAM access pattern table lookup (safe with a +// data cache). Internal C functions are static, casts added in slide_257(). +// mg_p256_*() wrappers are at the end + +#include "tls_p256.h" +#include "util.h" + +#if MG_P256 == MG_P256_LENNGREN && \ + (MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)) + +typedef const uint32_t (*constarr)[8]; + +struct FGInteger { + // To get the value this struct represents, + // interpret signed_value as a two's complement 288-bit little endian integer, + // and negate if flip_sign is -1 + int flip_sign; // 0 or -1 + uint32_t signed_value[9]; // of 288 bits, 257 are useful (top 31 bits are sign-extended from bit 256) +}; + +struct SignPrecomp { // p256_sign_step1() result + uint32_t r[8]; + uint32_t k_inv[8]; +}; + +struct XYInteger { + // To get the value this struct represents, + // interpret signed_value as an unsigned 288-bit little endian integer, + // and negate if flip_sign is -1 + int flip_sign; // 0 or -1 + uint32_t value[8]; // unsigned value, 0 <= value < P256_order +}; + +int P256_divsteps2_31(int delta, uint32_t f, uint32_t g, uint32_t res_matrix[4]); +void P256_matrix_mul_fg_9(uint32_t a, uint32_t b, const struct FGInteger fg[2], struct FGInteger *res); +void P256_matrix_mul_mod_n(uint32_t a, uint32_t b, const struct XYInteger xy[2], struct XYInteger *res); + +void P256_to_montgomery(uint32_t aR[8], const uint32_t a[8]); +void P256_from_montgomery(uint32_t a[8], const uint32_t aR[8]); +bool P256_check_range_p(const uint32_t a[8]); + +bool P256_check_range_n(const uint32_t a[8]); +void P256_mul_mod_n(uint32_t res[8], const uint32_t a[8], const uint32_t b[8]); +void P256_add_mod_n(uint32_t res[8], const uint32_t a[8], const uint32_t b[8]); +void P256_reduce_mod_n_32bytes(uint32_t res[8], const uint32_t a[8]); + +void P256_select_point(uint32_t (*output)[8], uint32_t* table, uint32_t num_coordinates, uint32_t index); + +void P256_jacobian_to_affine(uint32_t affine_mont_x[8], uint32_t affine_mont_y[8], const uint32_t jacobian_mont[3][8]); +bool P256_point_is_on_curve(const uint32_t x_mont[8], const uint32_t y_mont[8]); +void P256_double_j(uint32_t jacobian_point_out[3][8], const uint32_t jacobian_point_in[3][8]); +void P256_add_sub_j(uint32_t jacobian_point1[3][8], const uint32_t (*point2)[8], bool is_sub, bool p2_is_affine); +bool P256_verify_last_step(const uint32_t r[8], const uint32_t jacobian_point[3][8]); + +void P256_negate_mod_p_if(uint32_t out[8], const uint32_t in[8], uint32_t should_negate); +void P256_negate_mod_n_if(uint32_t out[8], const uint32_t in[8], uint32_t should_negate); + +extern uint32_t P256_order[9]; + +static const uint32_t one_montgomery[8] = {1, 0, 0, 0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe, 0}; + +// This table contains 1G, 3G, 5G, ... 15G in affine coordinates in montgomery form +static const uint32_t p256_basepoint_precomp[8][2][8] = { +{{0x18a9143c, 0x79e730d4, 0x5fedb601, 0x75ba95fc, 0x77622510, 0x79fb732b, 0xa53755c6, 0x18905f76}, +{0xce95560a, 0xddf25357, 0xba19e45c, 0x8b4ab8e4, 0xdd21f325, 0xd2e88688, 0x25885d85, 0x8571ff18}}, +{{0x4eebc127, 0xffac3f90, 0x87d81fb, 0xb027f84a, 0x87cbbc98, 0x66ad77dd, 0xb6ff747e, 0x26936a3f}, +{0xc983a7eb, 0xb04c5c1f, 0x861fe1a, 0x583e47ad, 0x1a2ee98e, 0x78820831, 0xe587cc07, 0xd5f06a29}}, +{{0xc45c61f5, 0xbe1b8aae, 0x94b9537d, 0x90ec649a, 0xd076c20c, 0x941cb5aa, 0x890523c8, 0xc9079605}, +{0xe7ba4f10, 0xeb309b4a, 0xe5eb882b, 0x73c568ef, 0x7e7a1f68, 0x3540a987, 0x2dd1e916, 0x73a076bb}}, +{{0xa0173b4f, 0x746354e, 0xd23c00f7, 0x2bd20213, 0xc23bb08, 0xf43eaab5, 0xc3123e03, 0x13ba5119}, +{0x3f5b9d4d, 0x2847d030, 0x5da67bdd, 0x6742f2f2, 0x77c94195, 0xef933bdc, 0x6e240867, 0xeaedd915}}, +{{0x264e20e8, 0x75c96e8f, 0x59a7a841, 0xabe6bfed, 0x44c8eb00, 0x2cc09c04, 0xf0c4e16b, 0xe05b3080}, +{0xa45f3314, 0x1eb7777a, 0xce5d45e3, 0x56af7bed, 0x88b12f1a, 0x2b6e019a, 0xfd835f9b, 0x86659cd}}, +{{0x6245e404, 0xea7d260a, 0x6e7fdfe0, 0x9de40795, 0x8dac1ab5, 0x1ff3a415, 0x649c9073, 0x3e7090f1}, +{0x2b944e88, 0x1a768561, 0xe57f61c8, 0x250f939e, 0x1ead643d, 0xc0daa89, 0xe125b88e, 0x68930023}}, +{{0x4b2ed709, 0xccc42563, 0x856fd30d, 0xe356769, 0x559e9811, 0xbcbcd43f, 0x5395b759, 0x738477ac}, +{0xc00ee17f, 0x35752b90, 0x742ed2e3, 0x68748390, 0xbd1f5bc1, 0x7cd06422, 0xc9e7b797, 0xfbc08769}}, +{{0xbc60055b, 0x72bcd8b7, 0x56e27e4b, 0x3cc23ee, 0xe4819370, 0xee337424, 0xad3da09, 0xe2aa0e43}, +{0x6383c45d, 0x40b8524f, 0x42a41b25, 0xd7663554, 0x778a4797, 0x64efa6de, 0x7079adf4, 0x2042170a}} +}; + +// This contains two tables, 8 points each in affine coordinates in montgomery form +// The first table contains these points: +// (2^192 - 2^128 - 2^64 - 1)G +// (2^192 - 2^128 - 2^64 + 1)G +// (2^192 - 2^128 + 2^64 - 1)G +// (2^192 - 2^128 + 2^64 + 1)G +// (2^192 + 2^128 - 2^64 - 1)G +// (2^192 + 2^128 - 2^64 + 1)G +// (2^192 + 2^128 + 2^64 - 1)G +// (2^192 + 2^128 + 2^64 + 1)G +// The second table contains the same points multiplied by 2^32 +static const uint32_t p256_basepoint_precomp2[2][8][2][8] = +{ +{ +{{0x670844e0, 0x52d8a7c9, 0xef68a29d, 0xe33bdc, 0x4bdb7361, 0xf3d2848, 0x91c5304d, 0x5222c821}, +{0xdf73fc25, 0xea6d2944, 0x255c81b, 0xa04c0f55, 0xefe488a8, 0x29acdc97, 0x80a560de, 0xbe2e158f}}, +{{0x2b13e673, 0xfc8511ee, 0xd103ed24, 0xffc58dee, 0xea7e99b8, 0x1022523a, 0x4afc8a17, 0x8f43ea39}, +{0xc5f33d0b, 0x8f4e2dbc, 0xd0aa1681, 0x3bc099fa, 0x79ff9df1, 0xffbb7b41, 0xd58b57c4, 0x180de09d}}, +{{0x8bd1cda5, 0x56430752, 0x8e05eda5, 0x1807577f, 0x956896e9, 0x99c699b, 0xf1f0efb5, 0x83d6093d}, +{0xed97061c, 0xef5af17e, 0x30d4c3c, 0x35b977b8, 0x49229439, 0x81fa75a2, 0xa0b6d35d, 0xf5a22070}}, +{{0x74f81cf1, 0x814c5365, 0x120065b, 0xe30baff7, 0x15132621, 0x80ae1256, 0x36a80788, 0x16d2b8cb}, +{0xecc50bca, 0x33d14697, 0x17aedd21, 0x19a9dfb0, 0xedc3f766, 0x523fbcc7, 0xb2cf5afd, 0x9c4de6dd}}, +{{0xcf0d9f6d, 0x5305a9e6, 0x81a9b021, 0x5839172f, 0x75c687cf, 0xcca7a4dd, 0x844be22f, 0x36d59b3e}, +{0x111a53e9, 0xcace7e62, 0xf063f3a1, 0x91c843d4, 0xda812da, 0xbf77e5f0, 0x437f3176, 0xe64af9c}}, +{{0xcf07517d, 0xdbd568bb, 0xba6830b9, 0x2f1afba2, 0xe6c4c2a6, 0x15b6807c, 0xe4966aef, 0x91c7eabc}, +{0xd6b2b6e6, 0x716dea1b, 0x19f85b4b, 0x248c43d1, 0x4a315e2a, 0x16dcfd60, 0xc72b3d0b, 0x15fdd303}}, +{{0x42b7dfd5, 0xe40bf9f4, 0x2d934f2a, 0x673689f3, 0x30a6f50b, 0x8314beb4, 0x976ec64e, 0xd17af2bc}, +{0x1ee7ddf1, 0x39f66c4f, 0x68ea373c, 0x7f68e18b, 0x53d0b186, 0x5166c1f2, 0x7be58f14, 0x95dda601}}, +{{0x42913074, 0xd5ae356, 0x48a542b1, 0x55491b27, 0xb310732a, 0x469ca665, 0x5f1a4cc1, 0x29591d52}, +{0xb84f983f, 0xe76f5b6b, 0x9f5f84e1, 0xbe7eef41, 0x80baa189, 0x1200d496, 0x18ef332c, 0x6376551f}} +}, +{ +{{0x7c4e54f5, 0xb9e5cbc0, 0xe1410e34, 0xc53a1a17, 0xec454425, 0x3e199130, 0x1700902e, 0xb029c97e}, +{0x786423b6, 0x2de66e11, 0xb41a95be, 0x262dc914, 0x451b683, 0x51766abd, 0x85bb6fb1, 0x55ad5f34}}, +{{0x9066cb79, 0x74f4f1c, 0x30c8b94e, 0x1ab31bd6, 0xd74275b3, 0x6d3f012f, 0x9ddcce40, 0xa214d0b1}, +{0xd165050a, 0x24aedf74, 0xe0e5dc3e, 0x95f17ece, 0xd9224456, 0x6ada9cda, 0x2dd60eea, 0x1fadb2d1}}, +{{0xe20cfb9b, 0xa3d83091, 0xba76e0cb, 0xae79c975, 0xc8858a6e, 0xa5f2a588, 0x874a3168, 0xe897a5f4}, +{0x7d48f096, 0xf6c1ef40, 0xc35b132c, 0x1f9c516b, 0x53c479fd, 0xe1040f91, 0x9df06743, 0x60e881f}}, +{{0x52a90e51, 0x9e0ad72, 0x38c50a96, 0xb7e66ea3, 0x7d997770, 0xab32ad05, 0x445671cb, 0xceaffe2}, +{0x5d37cc99, 0xdfbe753c, 0xe0fea2d5, 0x95d068cc, 0x4dd77cb6, 0x1e37cdda, 0x55530688, 0x88c5a4bb}}, +{{0xc7744f1, 0x3413f033, 0xbc816702, 0x23c05c89, 0x1192b5ac, 0x2322ee9a, 0x373180bb, 0xc1636a0}, +{0xbdde0207, 0xfe2f3d4, 0xc23578d8, 0xe1a093a, 0xc888ead, 0x6e5f0d1, 0x52a2b660, 0x9ca285a5}}, +{{0xce923964, 0xdae76995, 0xa34c7993, 0xcc96493a, 0xea73d9e7, 0xd19b5144, 0x311e6e34, 0x4a5c263}, +{0xd9a2a443, 0x7db5b32b, 0x2cfd960c, 0x3754bd33, 0xa430f15, 0xc5bcc98, 0xd9a94574, 0x5651201f}}, +{{0xfc0418fe, 0xebdd8921, 0x34e20036, 0x37015b39, 0xdf03a353, 0xcf4fcd8f, 0xf12cab16, 0xdc2de6e1}, +{0xd071df14, 0x9c17cc1a, 0x63415530, 0xd7c5e6a3, 0x68f3fb1e, 0xb5301660, 0x18269301, 0xb5f70bc9}}, +{{0x79ec1a0f, 0x2d8daefd, 0xceb39c97, 0x3bbcd6fd, 0x58f61a95, 0xf5575ffc, 0xadf7b420, 0xdbd986c4}, +{0x15f39eb7, 0x81aa8814, 0xb98d976c, 0x6ee2fcf5, 0xcf2f717d, 0x5465475d, 0x6860bbd0, 0x8e24d3c4}} +} +}; + +// Takes the leftmost 256 bits in hash (treated as big endian), +// and converts to little endian integer z. +static void hash_to_z(uint32_t z[8], const uint8_t* hash, uint32_t hashlen) { + if (hashlen > 32) { + hashlen = 32; + } + for (uint32_t i = 0; i < hashlen; i++) { + ((uint8_t*)z)[i] = hash[hashlen - 1 - i]; + } + for (uint32_t i = hashlen; i < 32; i++) { + ((uint8_t*)z)[i] = 0; + } +} + +// Creates a representation of a (little endian integer), +// so that r[0] + 2*r[1] + 2^2*r[2] + 2^3*r[3] + ... = a, +// where each r[i] is -15, -13, ..., 11, 13, 15 or 0. +// Only around 1/5.5 of the r[i] will be non-zero. +static void slide_257(signed char r[257], const uint8_t a[32]) { + for (int i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + r[256] = 0; + + for (int i = 0; i < 256; i++) { + if (r[i] != 0) { + for (int b = 1; b <= 4 && i + b < 256; b++) { + if (r[i + b] != 0) { + if (r[i] + (r[i + b] << b) <= 15) { + r[i] = (signed char) (r[i] + (r[i + b] << b)); r[i + b] = 0; + } else if (r[i] - (r[i + b] << b) >= -15) { + r[i] = (signed char) (r[i] - (r[i + b] << b)); + for (;;) { + r[i + b] = 0; + b++; + if (!r[i + b]) { + r[i + b] = 1; + b--; // Will be added back after loop footer b++ + break; + } + } + } else { + break; + } + } + } + } + } +} + +static void P256_mod_n_inv(uint32_t out[8], const uint32_t in[8]) { + // This function follows the algorithm in section 12.1 of https://gcd.cr.yp.to/safegcd-20190413.pdf. + // It has been altered in the following ways: + // 1. Due to 32-bit cpu, we use 24 * 31 iterations instead of 12 * 62. + // 2. P-256 modulus instead of 2^255-19. + // 744 iterations are still enough and slightly more than the required 741 (floor((49*256+57)/17)). + // 3. Step 5 has been corrected to go back to step 2 instead of step 3. + // 4. The order of the matrix multiplications in step 6 has been changed to (T24*(T23*(T22*(...*(T1*[0, 1]))))), + // where [0, 1] is a column vector to make it possible to be able to extract the "top-right corner", v, of T24*T23*...*T1. + // The result v will then be contained in the first element of the resulting column vector. + + struct { + struct FGInteger fg[2]; // f and g + struct XYInteger xy[2]; // x and y + } state[2]; // "current" and "next" + + state[0].fg[0].flip_sign = 0; // non-negative f + memcpy(&state[0].fg[0].signed_value, P256_order, 36); // f + state[0].fg[1].flip_sign = 0; // non-negative g + memcpy(&state[0].fg[1].signed_value, in, 32); // g + state[0].fg[1].signed_value[8] = 0; // upper bits of g are 0 + memset(&state[0].xy, 0, sizeof(state[0].xy)); + // We later need a factor 2^-744. The montgomery multiplication gives 2^(24*-32)=2^-768, so multiply the init value (1) by 2^24 here. + state[0].xy[1].value[0] = 1U << 24; + + int delta = 1; + for (int i = 0; i < 24; i++) { + // Scaled translation matrix Ti + uint32_t matrix[4]; // element range: [-2^30, 2^31] (negative numbers are stored in two's complement form) + + // Decode f and g into two's complement representation and use the lowest 32 bits in the P256_divsteps2_31 calculation + uint32_t negate_f = state[i % 2].fg[0].flip_sign; + uint32_t negate_g = state[i % 2].fg[1].flip_sign; + delta = P256_divsteps2_31(delta, (state[i % 2].fg[0].signed_value[0] ^ negate_f) - negate_f, (state[i % 2].fg[1].signed_value[0] ^ negate_g) - negate_g, matrix); + + // "Jump step", calculates the new f and g values that applies after 31 divstep2 iterations + P256_matrix_mul_fg_9(matrix[0], matrix[1], state[i % 2].fg, &state[(i + 1) % 2].fg[0]); + P256_matrix_mul_fg_9(matrix[2], matrix[3], state[i % 2].fg, &state[(i + 1) % 2].fg[1]); + + // Iterate the result vector + // Due to montgomery multiplication inside this function, each step also adds a 2^-32 factor + P256_matrix_mul_mod_n(matrix[0], matrix[1], state[i % 2].xy, &state[(i + 1) % 2].xy[0]); + P256_matrix_mul_mod_n(matrix[2], matrix[3], state[i % 2].xy, &state[(i + 1) % 2].xy[1]); + } + // Calculates val^-1 = sgn(f) * v * 2^-744, where v is the "top-right corner" of the resulting T24*T23*...*T1 matrix. + // In this implementation, at this point x contains v * 2^-744. + P256_negate_mod_n_if(out, &state[0].xy[0].value[0], (state[0].xy[0].flip_sign ^ state[0].fg[0].flip_sign ^ state[0].fg[0].signed_value[8]) & 1); +} + +// Constant time abs +static inline uint32_t abs_int(int8_t a) { + uint32_t a_u = (uint32_t)(int32_t)a; + uint32_t mask = a_u >> 31; + mask |= mask << 1; + mask |= mask << 2; + uint32_t result = (-a) & mask; + result |= a & (mask ^ 0xf); + return result; +} + +// Calculates scalar*P in constant time (except for the scalars 2 and n-2, for which the results take a few extra cycles to compute) +static void scalarmult_variable_base(uint32_t output_mont_x[8], uint32_t output_mont_y[8], const uint32_t input_mont_x[8], const uint32_t input_mont_y[8], const uint32_t scalar[8]) { + // Based on https://eprint.iacr.org/2014/130.pdf, Algorithm 1. + + uint32_t scalar2[8]; + int8_t e[64]; + + // The algorithm used requires the scalar to be odd. If even, negate the scalar modulo p to make it odd, and later negate the end result. + bool even = (scalar[0] & 1) ^ 1; + P256_negate_mod_n_if(scalar2, scalar, even); + + // Rewrite the scalar as e[0] + 2^4*e[1] + 2^8*e[2] + ... + 2^252*e[63], where each e[i] is an odd number and -15 <= e[i] <= 15. + e[0] = scalar2[0] & 0xf; + for (int i = 1; i < 64; i++) { + // Extract 4 bits + e[i] = (scalar2[i / 8] >> ((i % 8) * 4)) & 0xf; + // If even, subtract 2^4 from e[i - 1] and add 1 to e[i] + e[i - 1] -= (int8_t) (((e[i] & 1) ^ 1) << 4); + e[i] |= 1; + } + + // Create a table of P, 3P, 5P, ... 15P. + uint32_t table[8][3][8]; + memcpy(table[0][0], input_mont_x, 32); + memcpy(table[0][1], input_mont_y, 32); + memcpy(table[0][2], one_montgomery, 32); + P256_double_j(table[7], (constarr)table[0]); + for (int i = 1; i < 8; i++) { + memcpy(table[i], table[7], 96); + P256_add_sub_j(table[i], (constarr)table[i - 1], 0, 0); + } + + // Calculate the result as (((((((((e[63]*G)*2^4)+e[62])*2^4)+e[61])*2^4)...)+e[1])*2^4)+e[0] = (2^252*e[63] + 2^248*e[62] + ... + e[0])*G. + + uint32_t current_point[3][8]; + + // e[63] is never negative + P256_select_point(current_point, (uint32_t*)table, 3, e[63] >> 1); + + for (uint32_t i = 63; i --> 0;) { + for (int j = 3; j >= 0; j--) { + P256_double_j(current_point, (constarr)current_point); + } + uint32_t selected_point[3][8]; + P256_select_point(selected_point, (uint32_t*)table, 3, abs_int(e[i]) >> 1); + P256_negate_mod_p_if(selected_point[1], selected_point[1], (uint8_t)e[i] >> 7); + + // There is (only) one odd input scalar that leads to an exception when i == 0: n-2, + // in that case current_point will be equal to selected_point and hence a doubling + // will occur instead. We don't bother fixing the same constant time for that case since + // the probability of that random value to be generated is around 1/2^255 and an + // attacker could easily test this case anyway. + P256_add_sub_j(current_point, (constarr)selected_point, false, false); + } + P256_jacobian_to_affine(output_mont_x, output_mont_y, (constarr)current_point); + + // If the scalar was initially even, we now negate the result to get the correct result, since -(scalar*G) = (-scalar*G). + // This is done by negating y, since -(x,y) = (x,-y). + P256_negate_mod_p_if(output_mont_y, output_mont_y, even); +} + +#define get_bit(arr, i) ((arr[(i) / 32] >> ((i) % 32)) & 1) + +// Calculates scalar*G in constant time +static void scalarmult_fixed_base(uint32_t output_mont_x[8], uint32_t output_mont_y[8], const uint32_t scalar[8]) { + uint32_t scalar2[8]; + + // Just as with the algorithm used in variable base scalar multiplication, this algorithm requires the scalar to be odd. + bool even = (scalar[0] & 1) ^ 1; + P256_negate_mod_n_if(scalar2, scalar, even); + + // This algorithm conceptually rewrites the odd scalar as s[0] + 2^1*s[1] + 2^2*s[2] + ... + 2^255*s[255], where each s[i] is -1 or 1. + // By initially setting s[i] to the corresponding bit S[i] in the original odd scalar S, we go from lsb to msb, and whenever a value s[i] is 0, + // increase s[i] by 1 and decrease s[i-1] by 2. + // This will result in that s[i] = S[i+1] == 1 ? 1 : -1 for i < 255, and s[255] = 1. + + // We then form the scalars abs(s[j] + s[j+64]*2^64 + s[j+128]*2^128 + s[j+192]*2^192)*(2^32 * floor(j / 32)) for different 0 <= j < 64. + // Each scalar times G has already been precomputed in p256_basepoint_precomp2. + // That way we only need 31 point doublings and 63 point additions. + + uint32_t current_point[3][8]; + uint32_t selected_point[2][8]; + + for (uint32_t i = 32; i --> 0;) { + { + uint32_t mask = get_bit(scalar2, i + 32 + 1) | (get_bit(scalar2, i + 64 + 32 + 1) << 1) | (get_bit(scalar2, i + 2 * 64 + 32 + 1) << 2); + if (i == 31) { + P256_select_point(current_point, (uint32_t*)p256_basepoint_precomp2[1], 2, mask); + memcpy(current_point[2], one_montgomery, 32); + } else { + P256_double_j(current_point, (constarr)current_point); + + uint32_t sign = get_bit(scalar2, i + 3 * 64 + 32 + 1) - 1; // positive: 0, negative: -1 + mask = (mask ^ sign) & 7; + P256_select_point(selected_point, (uint32_t*)p256_basepoint_precomp2[1], 2, mask); + P256_negate_mod_p_if(selected_point[1], selected_point[1], sign & 1); + P256_add_sub_j(current_point, (constarr)selected_point, false, true); + } + } + { + uint32_t mask = get_bit(scalar2, i + 1) | (get_bit(scalar2, i + 64 + 1) << 1) | (get_bit(scalar2, i + 2 * 64 + 1) << 2); + uint32_t sign = get_bit(scalar2, i + 3 * 64 + 1) - 1; // positive: 0, negative: -1 + mask = (mask ^ sign) & 7; + P256_select_point(selected_point, (uint32_t*)p256_basepoint_precomp2[0], 2, mask); + P256_negate_mod_p_if(selected_point[1], selected_point[1], sign & 1); + P256_add_sub_j(current_point, (constarr)selected_point, false, true); + } + } + P256_jacobian_to_affine(output_mont_x, output_mont_y, (constarr)current_point); + + // Negate final result if the scalar was initially even. + P256_negate_mod_p_if(output_mont_y, output_mont_y, even); +} + +static void p256_convert_endianness(void* output, const void* input, size_t byte_len) { + for (size_t i = 0; i < byte_len / 2; i++) { + uint8_t t = ((uint8_t*)input)[byte_len - 1 - i]; + ((uint8_t*)output)[byte_len - 1 - i] = ((uint8_t*)input)[i]; + ((uint8_t*)output)[i] = t; + } +} + +static bool p256_verify(const uint32_t public_key_x[8], const uint32_t public_key_y[8], const uint8_t* hash, uint32_t hashlen_in_bytes, const uint32_t r[8], const uint32_t s[8]) { + if (!P256_check_range_n(r) || !P256_check_range_n(s)) { + return false; + } + + if (!P256_check_range_p(public_key_x) || !P256_check_range_p(public_key_y)) { + return false; + } + + uint32_t pk_table[8][3][8]; + P256_to_montgomery(pk_table[0][0], public_key_x); + P256_to_montgomery(pk_table[0][1], public_key_y); + memcpy(pk_table[0][2], one_montgomery, 32); + + if (!P256_point_is_on_curve(pk_table[0][0], pk_table[0][1])) { + return false; + } + + // Create a table of P, 3P, 5P, ..., 15P, where P is the public key. + P256_double_j(pk_table[7], (constarr)pk_table[0]); + for (int i = 1; i < 8; i++) { + memcpy(pk_table[i], pk_table[7], 96); + P256_add_sub_j(pk_table[i], (constarr)pk_table[i - 1], 0, 0); + } + + uint32_t z[8], w[8], u1[8], u2[8]; + + hash_to_z(z, hash, hashlen_in_bytes); + + P256_mod_n_inv(w, s); + + P256_mul_mod_n(u1, z, w); + P256_mul_mod_n(u2, r, w); + + // Each value in these arrays will be an odd integer v, so that -15 <= v <= 15. + // Around 1/5.5 of them will be non-zero. + signed char slide_bp[257], slide_pk[257]; + slide_257(slide_bp, (uint8_t*)u1); + slide_257(slide_pk, (uint8_t*)u2); + + uint32_t cp[3][8] = {0}; + + for (int i = 256; i >= 0; i--) { + P256_double_j(cp, (constarr)cp); + if (slide_bp[i] > 0) { + P256_add_sub_j(cp, p256_basepoint_precomp[slide_bp[i]/2], 0, 1); + } else if (slide_bp[i] < 0) { + P256_add_sub_j(cp, p256_basepoint_precomp[(-slide_bp[i])/2], 1, 1); + } + if (slide_pk[i] > 0) { + P256_add_sub_j(cp, (constarr)pk_table[slide_pk[i]/2], 0, 0); + } else if (slide_pk[i] < 0) { + P256_add_sub_j(cp, (constarr)pk_table[(-slide_pk[i])/2], 1, 0); + } + } + + return P256_verify_last_step(r, (constarr)cp); +} + +static bool p256_sign_step1(struct SignPrecomp *result, const uint32_t k[8]) { + do { + uint32_t point_res[2][8]; + if (!P256_check_range_n(k)) { + break; + } + scalarmult_fixed_base(point_res[0], point_res[1], k); + P256_mod_n_inv(result->k_inv, k); + P256_from_montgomery(result->r, point_res[0]); + P256_reduce_mod_n_32bytes(result->r, result->r); + + uint32_t r_sum = 0; + for (int i = 0; i < 8; i++) { + r_sum |= result->r[i]; + } + if (r_sum == 0) { + break; + } + return true; + } while (false); + + memset(result, 0, sizeof(struct SignPrecomp)); + return false; +} + +static bool p256_sign_step2(uint32_t r[8], uint32_t s[8], const uint8_t* hash, uint32_t hashlen_in_bytes, const uint32_t private_key[8], struct SignPrecomp *sign_precomp) { + do { + if (!P256_check_range_n(sign_precomp->k_inv) || !P256_check_range_n(sign_precomp->r)) { // just make sure user did not input an obviously invalid precomp + break; + } + uint32_t *const z = r; + hash_to_z(z, hash, hashlen_in_bytes); + P256_mul_mod_n(s, sign_precomp->r, private_key); + P256_add_mod_n(s, z, s); + P256_mul_mod_n(s, sign_precomp->k_inv, s); + + memcpy(r, sign_precomp->r, 32); + + uint32_t s_sum = 0; + for (int i = 0; i < 8; i++) { + s_sum |= s[i]; + } + if (s_sum == 0) { + break; + } + memset(sign_precomp, 0, sizeof(*sign_precomp)); + return true; + } while (false); + + memset(r, 0, 32); + memset(s, 0, 32); + return false; +} + +static bool p256_sign(uint32_t r[8], uint32_t s[8], const uint8_t* hash, uint32_t hashlen_in_bytes, const uint32_t private_key[8], const uint32_t k[8]) { + struct SignPrecomp t; + if (!p256_sign_step1(&t, k)) { + memset(r, 0, 32); + memset(s, 0, 32); + return false; + } + return p256_sign_step2(r, s, hash, hashlen_in_bytes, private_key, &t); +} + +static bool p256_scalarmult_base(uint32_t result_x[8], uint32_t result_y[8], const uint32_t scalar[8]) { + if (!P256_check_range_n(scalar)) { + return false; + } + scalarmult_fixed_base(result_x, result_y, scalar); + P256_from_montgomery(result_x, result_x); + P256_from_montgomery(result_y, result_y); + return true; + +} + +static bool p256_keygen(uint32_t public_key_x[8], uint32_t public_key_y[8], const uint32_t private_key[8]) { + return p256_scalarmult_base(public_key_x, public_key_y, private_key); +} + + +static bool p256_scalarmult_generic_no_scalar_check(uint32_t output_mont_x[8], uint32_t output_mont_y[8], const uint32_t scalar[8], const uint32_t in_x[8], const uint32_t in_y[8]) { + if (!P256_check_range_p(in_x) || !P256_check_range_p(in_y)) { + return false; + } + + P256_to_montgomery(output_mont_x, in_x); + P256_to_montgomery(output_mont_y, in_y); + + if (!P256_point_is_on_curve(output_mont_x, output_mont_y)) { + return false; + } + + scalarmult_variable_base(output_mont_x, output_mont_y, output_mont_x, output_mont_y, scalar); + return true; +} + +static bool p256_ecdh_calc_shared_secret(uint8_t shared_secret[32], const uint32_t private_key[8], const uint32_t others_public_key_x[8], const uint32_t others_public_key_y[8]) { + uint32_t result_x[8], result_y[8]; + if (!p256_scalarmult_generic_no_scalar_check(result_x, result_y, private_key, others_public_key_x, others_public_key_y)) { + return false; + } + P256_from_montgomery(result_x, result_x); + p256_convert_endianness(shared_secret, result_x, 32); + return true; +} + +// Assembler, from p256-cortex-m4-asm-gcc.S, converted to a top-level asm +// block so it can live in the amalgamated mongoose.c +__asm__( + ".pushsection .text.mg_p256,\"ax\",%progbits\n" + "\t.syntax unified\n" + "\t.thumb\n" + "\t.align 2\n" + "\t.type P256_select_point, %function\n" + "P256_select_point:\n" + "\t.global P256_select_point\n" + "\tpush {r0,r2,r3,r4-r11,lr}\n" + "\tsubs r2,#1\n" + "\tlsls r2,#5\n" + "0:\n" + "\trsbs r3,#0\n" + "\tsbcs r3,r3,r3\n" + "\tmvns r3,r3\n" + "\tldm r1!,{r6-r12,lr}\n" + "\tands r6,r3\n" + "\tands r7,r3\n" + "\tand r8,r3\n" + "\tand r9,r3\n" + "\tand r10,r3\n" + "\tand r11,r3\n" + "\tand r12,r3\n" + "\tand lr,r3\n" + "\tadds r1,r2\n" + "\tmovs r3,#1\n" + "1:\n" + "\tldr r0,[sp,#8]\n" + "\teors r0,r3\n" + "\tmrs r0,apsr\n" + "\tlsrs r0,#30\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r6,r2,r0,r4\n" + "\tumlal r7,r3,r0,r5\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r8,r2,r0,r4\n" + "\tumlal r9,r3,r0,r5\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r10,r2,r0,r4\n" + "\tumlal r11,r3,r0,r5\n" + "\tldm r1!,{r4,r5}\n" + "\tumlal r12,r2,r0,r4\n" + "\tumlal lr,r3,r0,r5\n" + "\tadds r1,r2\n" + "\tadds r3,#1\n" + "\tcmp r3,#8\n" + "\tbne 1b\n" + "\tldm sp,{r0,r4}\n" + "\tstm r0!,{r6-r12,lr}\n" + "\tstr r0,[sp]\n" + "\tsub r1,r1,r2, lsl #3\n" + "\tsubs r1,#224\n" + "\tsubs r4,#1\n" + "\tstr r4,[sp,#4]\n" + "\tldr r3,[sp,#8]\n" + "\tbne 0b\n" + "\tadd sp,#12\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_select_point, .-P256_select_point\n" + "\t.type mul288x288, %function\n" + "mul288x288:\n" + "\tpush {r4-r11,lr}\n" + "\tmov r4,r0\n" + "\tmov r5,r2\n" + "\tmov r6,r1\n" + "\tmovs r1,#72\n" + "\tbl setzero\n" + "\tldm r5,{r0-r2,r8-r12,lr}\n" + "\tmovs r7,#9\n" + "0:\n" + "\tldm r6!,{r5}\n" + "\tpush {r6,r7}\n" + "\tmovs r3,#0\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r0\n" + "\tumaal r7,r3,r5,r1\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r2\n" + "\tumaal r7,r3,r5,r8\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r9\n" + "\tumaal r7,r3,r5,r10\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6,r7}\n" + "\tumaal r6,r3,r5,r11\n" + "\tumaal r7,r3,r5,r12\n" + "\tstm r4!,{r6,r7}\n" + "\tldm r4,{r6}\n" + "\tumaal r3,r6,r5,lr\n" + "\tstm r4!,{r3,r6}\n" + "\tsubs r4,r4,#36\n" + "\tpop {r6,r7}\n" + "\tsubs r7,r7,#1\n" + "\tbne 0b\n" + "\tpop {r4-r11,pc}\n" + "\t.size mul288x288, .-mul288x288\n" + "\t.type setzero, %function\n" + "setzero:\n" + "\tmovs r2,#0\n" + "\tmovs r3,#0\n" + "0:\n" + "\tstm r0!,{r2,r3}\n" + "\tsubs r1,r1,#8\n" + "\tbne 0b\n" + "\tbx lr\n" + "\t.size setzero, .-setzero\n" + "\t.type P256_mulmod, %function\n" + "P256_mulmod:\n" + "\tpush {r2,lr}\n" + "\tsub sp,#28\n" + "\tldm r2,{r2,r3,r4,r5}\n" + "\tldm r1!,{r0,r10,lr}\n" + "\tumull r6,r11,r2,r0\n" + "\tumull r7,r12,r3,r0\n" + "\tumaal r7,r11,r2,r10\n" + "\tpush {r6,r7}\n" + "\tumull r8,r6,r4,r0\n" + "\tumaal r8,r11,r3,r10\n" + "\tumull r9,r7,r5,r0\n" + "\tumaal r9,r11,r4,r10\n" + "\tumaal r11,r7,r5,r10\n" + "\tumaal r8,r12,r2,lr\n" + "\tumaal r9,r12,r3,lr\n" + "\tumaal r11,r12,r4,lr\n" + "\tumaal r12,r7,r5,lr\n" + "\tldm r1!,{r0,r10,lr}\n" + "\tumaal r9,r6,r2,r0\n" + "\tumaal r11,r6,r3,r0\n" + "\tumaal r12,r6,r4,r0\n" + "\tumaal r6,r7,r5,r0\n" + "\tstrd r8,r9,[sp,#8]\n" + "\tmov r9,#0\n" + "\tumaal r11,r9,r2,r10\n" + "\tumaal r12,r9,r3,r10\n" + "\tumaal r6,r9,r4,r10\n" + "\tumaal r7,r9,r5,r10\n" + "\tmov r10,#0\n" + "\tumaal r12,r10,r2,lr\n" + "\tumaal r6,r10,r3,lr\n" + "\tumaal r7,r10,r4,lr\n" + "\tumaal r9,r10,r5,lr\n" + "\tldr r8,[r1],#4\n" + "\tmov lr,#0\n" + "\tumaal lr,r6,r2,r8\n" + "\tumaal r7,r6,r3,r8\n" + "\tumaal r9,r6,r4,r8\n" + "\tumaal r10,r6,r5,r8\n" + "\tldr r8,[r1],#-28\n" + "\tmov r0,#0\n" + "\tumaal r7,r0,r2,r8\n" + "\tumaal r9,r0,r3,r8\n" + "\tumaal r10,r0,r4,r8\n" + "\tumaal r6,r0,r5,r8\n" + "\tpush {r0}\n" + "\tldr r2,[sp,#40]\n" + "\tadds r2,r2,#16\n" + "\tldm r2,{r2,r3,r4,r5}\n" + "\tldr r8,[r1],#4\n" + "\tmov r0,#0\n" + "\tumaal r11,r0,r2,r8\n" + "\tstr r11,[sp,#16+4]\n" + "\tumaal r12,r0,r3,r8\n" + "\tumaal lr,r0,r4,r8\n" + "\tumaal r0,r7,r5,r8\n" + "\tldr r8,[r1],#4\n" + "\tmov r11,#0\n" + "\tumaal r12,r11,r2,r8\n" + "\tstr r12,[sp,#20+4]\n" + "\tumaal lr,r11,r3,r8\n" + "\tumaal r0,r11,r4,r8\n" + "\tumaal r11,r7,r5,r8\n" + "\tldr r8,[r1],#4\n" + "\tmov r12,#0\n" + "\tumaal lr,r12,r2,r8\n" + "\tstr lr,[sp,#24+4]\n" + "\tumaal r0,r12,r3,r8\n" + "\tumaal r11,r12,r4,r8\n" + "\tumaal r10,r12,r5,r8\n" + "\tldr r8,[r1],#4\n" + "\tmov lr,#0\n" + "\tumaal r0,lr,r2,r8\n" + "\tstr r0,[sp,#28+4]\n" + "\tumaal r11,lr,r3,r8\n" + "\tumaal r10,lr,r4,r8\n" + "\tumaal r6,lr,r5,r8\n" + "\tldm r1!,{r0,r8}\n" + "\tumaal r11,r9,r2,r0\n" + "\tstr r11,[sp,#32+4]\n" + "\tumaal r9,r10,r3,r0\n" + "\tumaal r10,r6,r4,r0\n" + "\tpop {r11}\n" + "\tumaal r11,r6,r5,r0\n" + "\tumaal r9,r7,r2,r8\n" + "\tumaal r10,r7,r3,r8\n" + "\tumaal r11,r7,r4,r8\n" + "\tumaal r6,r7,r5,r8\n" + "\tldm r1!,{r0,r8}\n" + "\tumaal r10,r12,r2,r0\n" + "\tumaal r11,r12,r3,r0\n" + "\tumaal r6,r12,r4,r0\n" + "\tumaal r7,r12,r5,r0\n" + "\tumaal r11,lr,r2,r8\n" + "\tumaal lr,r6,r3,r8\n" + "\tumaal r6,r7,r4,r8\n" + "\tumaal r7,r12,r5,r8\n" + "\tpush {r6,r7,r12}\n" + "\tadd r7,sp,#12\n" + "\tldm r7,{r0-r8}\n" + "\tmov r12,#0\n" + "\tadds r3,r0\n" + "\tadcs r4,r4,r1\n" + "\tadcs r5,r5,r2\n" + "\tadcs r6,r6,r0\n" + "\tadcs r7,r7,r1\n" + "\tadcs r8,r8,r0\n" + "\tadcs r9,r9,r1\n" + "\tadcs r10,r10,#0\n" + "\tadcs r11,r11,#0\n" + "\tadcs r12,r12,#0\n" + "\tadds r6,r3\n" + "\tadcs r7,r7,r4\n" + "\tadcs r8,r8,r2\n" + "\tadcs r9,r9,r3\n" + "\tadcs r10,r10,r2\n" + "\tadcs r11,r11,r3\n" + "\tadcs r12,r12,#0\n" + "\tsubs r7,r0\n" + "\tsbcs r8,r8,r1\n" + "\tsbcs r9,r9,r2\n" + "\tsbcs r10,r10,r3\n" + "\tsbcs r11,r11,#0\n" + "\tsbcs r12,r12,#0\n" + "\tpop {r1-r3}\n" + "\tadds r0,lr,r12\n" + "\tadcs r1,r1,#0\n" + "\tmov r12,#0\n" + "\tadcs r12,r12,#0\n" + "\tadcs r8,r8,r5\n" + "\tadcs r9,r9,r6\n" + "\tadcs r10,r10,r4\n" + "\tadcs r11,r11,r5\n" + "\tadcs r0,r0,r4\n" + "\tadcs r1,r1,r5\n" + "\tadcs r2,r2,r12\n" + "\tadcs r3,r3,#0\n" + "\tmov r12,#0\n" + "\tadcs r12,r12,#0\n" + "\tadcs r10,r10,r7\n" + "\tadcs r11,r11,#0\n" + "\tadcs r0,r0,r6\n" + "\tadcs r1,r1,r7\n" + "\tadcs r2,r2,r6\n" + "\tadcs r3,r3,r7\n" + "\tadcs r12,r12,#0\n" + "\tsubs r11,r4\n" + "\tsbcs r0,r0,r5\n" + "\tsbcs r1,r1,r6\n" + "\tsbcs r2,r2,r7\n" + "\tsbcs r3,r3,#0\n" + "\tsbcs r12,r12,#0\n" + "\tsubs r8,r8,#0xffffffff\n" + "\tsbcs r9,r9,#0xffffffff\n" + "\tsbcs r10,r10,#0xffffffff\n" + "\tsbcs r11,r11,#0\n" + "\tsbcs r4,r0,#0\n" + "\tsbcs r5,r1,#0\n" + "\tsbcs r6,r2,#1\n" + "\tsbcs r7,r3,#0xffffffff\n" + "\tsbc r12,r12,#0\n" + "\tadds r0,r8,r12\n" + "\tadcs r1,r9,r12\n" + "\tadcs r2,r10,r12\n" + "\tadcs r3,r11,#0\n" + "\tadcs r4,r4,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r6,r12, lsr #31\n" + "\tadcs r7,r7,r12\n" + "\tadd sp,sp,#40\n" + "\tpop {pc}\n" + "\t.size P256_mulmod, .-P256_mulmod\n" + "\t.type P256_sqrmod, %function\n" + "P256_sqrmod:\n" + "\tpush {lr}\n" + "\tumull r9,r10,r0,r0\n" + "\tumull r11,r12,r0,r1\n" + "\tadds r11,r11,r11\n" + "\tmov lr,#0\n" + "\tumaal r10,r11,lr,lr\n" + "\tpush {r9,r10}\n" + "\tmov r9,#0\n" + "\tumaal r9,r12,r0,r2\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,r1,r1\n" + "\tpush {r9}\n" + "\tumull r9,r10,r0,r3\n" + "\tumaal r9,r12,r1,r2\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,lr,lr\n" + "\tpush {r9}\n" + "\tmov r9,#0\n" + "\tumaal r9,r10,r0,r4\n" + "\tumaal r9,r12,r1,r3\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,r2,r2\n" + "\tpush {r9}\n" + "\tumull r9,r8,r0,r5\n" + "\tumaal r9,r10,r1,r4\n" + "\tumaal r9,r12,r2,r3\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,lr,lr\n" + "\tpush {r9}\n" + "\tmov r9,#0\n" + "\tumaal r9,r8,r1,r5\n" + "\tumaal r9,r12,r2,r4\n" + "\tumaal r9,r10,r0,r6\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,r3,r3\n" + "\tpush {r9}\n" + "\tumull r9,r0,r0,r7\n" + "\tumaal r9,r10,r1,r6\n" + "\tumaal r9,r12,r2,r5\n" + "\tumaal r9,r8,r3,r4\n" + "\tadcs r9,r9,r9\n" + "\tumaal r9,r11,lr,lr\n" + "\tumaal r0,r8,r1,r7\n" + "\tumaal r0,r10,r2,r6\n" + "\tumaal r0,r12,r3,r5\n" + "\tadcs r0,r0,r0\n" + "\tumaal r11,r0,r4,r4\n" + "\tumaal r12,r8,r2,r7\n" + "\tumaal r12,r10,r3,r6\n" + "\tmovs r2,#0\n" + "\tumaal r12,r2,r4,r5\n" + "\tadcs r1,r12,r12\n" + "\tumaal r0,r1,lr,lr\n" + "\tumaal r2,r8,r3,r7\n" + "\tumaal r2,r10,r4,r6\n" + "\tadcs r2,r2,r2\n" + "\tumaal r1,r2,r5,r5\n" + "\tmovs r3,#0\n" + "\tumaal r3,r8,r4,r7\n" + "\tumaal r3,r10,r5,r6\n" + "\tadcs r3,r3,r3\n" + "\tumaal r2,r3,lr,lr\n" + "\tumaal r8,r10,r5,r7\n" + "\tadcs r8,r8,r8\n" + "\tumaal r3,r8,r6,r6\n" + "\tumull r4,r5,lr,lr\n" + "\tumaal r4,r10,r6,r7\n" + "\tadcs r4,r4,r4\n" + "\tumaal r4,r8,lr,lr\n" + "\tadcs r10,r10,r10\n" + "\tumaal r8,r10,r7,r7\n" + "\tadcs r10,r10,lr\n" + "\tpush {r4,r8,r10}\n" + "\tadd r4,sp,#12\n" + "\tldm r4,{r4-r8,r10,r12}\n" + "\tX0 .req r10\n" + "\tX1 .req r12\n" + "\tX2 .req r8\n" + "\tX3 .req r7\n" + "\tX4 .req r6\n" + "\tX5 .req r5\n" + "\tX6 .req r4\n" + "\tX7 .req r9\n" + "\tX8 .req r11\n" + "\tX9 .req r0\n" + "\tX10 .req r1\n" + "\tX11 .req r2\n" + "\tX12 .req r3\n" + "\tX13 .req r7\n" + "\tX14 .req r8\n" + "\tX15 .req r12\n" + "\tadcs X3,X3,X0\n" + "\tadcs X4,X4,X1\n" + "\tadcs X5,X5,X2\n" + "\tadcs X6,X6,X0\n" + "\tadcs X7,X7,X1\n" + "\tadcs X8,X8,X0\n" + "\tadcs X9,X9,X1\n" + "\tadcs X10,X10,#0\n" + "\tadcs X11,X11,#0\n" + "\tadcs lr,lr,#0\n" + "\tadds X6,X3\n" + "\tadcs X7,X7,X4\n" + "\tadcs X8,X8,X2\n" + "\tadcs X9,X9,X3\n" + "\tadcs X10,X10,X2\n" + "\tadcs X11,X11,X3\n" + "\tadcs lr,lr,#0\n" + "\tsubs X7,X0\n" + "\tsbcs X8,X8,X1\n" + "\tsbcs X9,X9,X2\n" + "\tsbcs X10,X10,X3\n" + "\tsbcs X11,X11,#0\n" + "\tsbcs lr,lr,#0\n" + "\tpop {X13,X14,X15}\n" + "\tadds X0,X12,lr\n" + "\tadcs X13,X13,#0\n" + "\tmov lr,#0\n" + "\tadcs lr,lr,#0\n" + "\tadcs X8,X8,X5\n" + "\tadcs X9,X9,X6\n" + "\tadcs X10,X10,X4\n" + "\tadcs X11,X11,X5\n" + "\tadcs X0,X0,X4\n" + "\tadcs X13,X13,X5\n" + "\tadcs X14,X14,lr\n" + "\tadcs X15,X15,#0\n" + "\tmov lr,#0\n" + "\tadcs lr,lr,#0\n" + "\tadcs X10,X10,X7\n" + "\tadcs X11,X11,#0\n" + "\tadcs X0,X0,X6\n" + "\tadcs X13,X13,X7\n" + "\tadcs X14,X14,X6\n" + "\tadcs X15,X15,X7\n" + "\tadcs lr,lr,#0\n" + "\tsubs X11,X4\n" + "\tsbcs X0,X0,X5\n" + "\tsbcs X13,X13,X6\n" + "\tsbcs X14,X14,X7\n" + "\tsbcs X15,X15,#0\n" + "\tsbcs lr,lr,#0\n" + "\tsubs r11,r11,#0xffffffff\n" + "\tsbcs r9,r0,#0xffffffff\n" + "\tsbcs r4,r1,#0xffffffff\n" + "\tsbcs r3,r2,#0\n" + "\tsbcs r6,r10,#0\n" + "\tsbcs r5,r7,#0\n" + "\tsbcs r10,r8,#1\n" + "\tsbcs r8,r12,#0xffffffff\n" + "\tsbcs r7,lr,#0\n" + "\tadds r0,r11,r7\n" + "\tadcs r1,r9,r7\n" + "\tadcs r2,r4,r7\n" + "\tadcs r3,r3,#0\n" + "\tadcs r4,r6,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r10,r7, lsr #31\n" + "\tadcs r7,r8,r7\n" + "\tadd sp,#28\n" + "\tpop {pc}\n" + "\t.size P256_sqrmod, .-P256_sqrmod\n" + "\t.type P256_submod, %function\n" + "P256_submod:\n" + "\tldm r1,{r3-r10}\n" + "\tldm r2!,{r0,r1,r11,r12}\n" + "\tsubs r3,r0\n" + "\tsbcs r4,r4,r1\n" + "\tsbcs r5,r5,r11\n" + "\tsbcs r6,r6,r12\n" + "\tldm r2,{r0,r1,r11,r12}\n" + "\tsbcs r7,r7,r0\n" + "\tsbcs r8,r8,r1\n" + "\tsbcs r9,r9,r11\n" + "\tsbcs r10,r10,r12\n" + "\tsbcs r11,r11,r11\n" + "\tadds r0,r3,r11\n" + "\tadcs r1,r4,r11\n" + "\tadcs r2,r5,r11\n" + "\tadcs r3,r6,#0\n" + "\tadcs r4,r7,#0\n" + "\tadcs r5,r8,#0\n" + "\tadcs r6,r9,r11, lsr #31\n" + "\tadcs r7,r10,r11\n" + "\tbx lr\n" + "\t.size P256_submod, .-P256_submod\n" + "\t.type P256_addmod, %function\n" + "P256_addmod:\n" + "\tldm r2,{r2-r9}\n" + "\tldm r1!,{r0,r10,r11,r12}\n" + "\tadds r2,r0\n" + "\tadcs r3,r3,r10\n" + "\tadcs r4,r4,r11\n" + "\tadcs r5,r5,r12\n" + "\tldm r1,{r0,r1,r11,r12}\n" + "\tadcs r6,r6,r0\n" + "\tadcs r7,r7,r1\n" + "\tadcs r8,r8,r11\n" + "\tadcs r9,r9,r12\n" + "\tmovs r10,#0\n" + "\tadcs r10,r10,r10\n" + "\tsubs r2,#0xffffffff\n" + "\tsbcs r3,r3,#0xffffffff\n" + "\tsbcs r4,r4,#0xffffffff\n" + "\tsbcs r5,r5,#0\n" + "\tsbcs r6,r6,#0\n" + "\tsbcs r7,r7,#0\n" + "\tsbcs r8,r8,#1\n" + "\tsbcs r9,r9,#0xffffffff\n" + "\tsbcs r10,r10,#0\n" + "\tadds r0,r2,r10\n" + "\tadcs r1,r3,r10\n" + "\tadcs r2,r4,r10\n" + "\tadcs r3,r5,#0\n" + "\tadcs r4,r6,#0\n" + "\tadcs r5,r7,#0\n" + "\tadcs r6,r8,r10, lsr #31\n" + "\tadcs r7,r9,r10\n" + "\tbx lr\n" + "\t.size P256_addmod, .-P256_addmod\n" + "\t.type P256_sqrmod_many, %function\n" + "P256_sqrmod_many:\n" + "\tpush {r8,lr}\n" + "0:\n" + "\tbl P256_sqrmod\n" + "\tldr r8,[sp,#0]\n" + "\tsubs r8,r8,#1\n" + "\tstr r8,[sp,#0]\n" + "\tbne 0b\n" + "\tpop {r8,pc}\n" + "\t.size P256_sqrmod_many, .-P256_sqrmod_many\n" + "\t.type P256_sqrmod_many_and_mulmod, %function\n" + "P256_sqrmod_many_and_mulmod:\n" + "\tpush {r9,lr}\n" + "\tbl P256_sqrmod_many\n" + "\tpush {r0-r7}\n" + "\tmov r1,sp\n" + "\tldr r2,[sp,#32]\n" + "\tbl P256_mulmod\n" + "\tadd sp,#36\n" + "\tpop {pc}\n" + "\t.size P256_sqrmod_many_and_mulmod, .-P256_sqrmod_many_and_mulmod\n" + "\t.type P256_modinv_sqrt, %function\n" + "P256_modinv_sqrt:\n" + "\tpush {r0-r8,lr}\n" + "\tmov r8,#1\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tmov r1,sp\n" + "\tadd r2,sp,#32\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmov r8,#8-4\n" + "\tadd r9,sp,#32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tmov r8,#16-8\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tmov r8,#16\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tpush {r0-r7}\n" + "\tmov r8,#32\n" + "\tadd r9,sp,#5*32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tldr r8,[sp,#6*32]\n" + "\tcmp r8,#0\n" + "\tbne 0f\n" + "\tmov r8,#192-64\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#224-192\n" + "\tmov r9,sp\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#240-224\n" + "\tadd r9,sp,#32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#248-240\n" + "\tadd r9,sp,#64\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#252-248\n" + "\tadd r9,sp,#128\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#256-252\n" + "\tadd r9,sp,#96\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tmov r1,sp\n" + "\tadd r2,sp,#5*32\n" + "\tbl P256_mulmod\n" + "\tb 1f\n" + "0:\n" + "\tmov r8,#160-64\n" + "\tadd r9,sp,#5*32\n" + "\tbl P256_sqrmod_many_and_mulmod\n" + "\tmov r8,#254-160\n" + "\tbl P256_sqrmod_many\n" + "1:\n" + "\tadd sp,#6*32+4\n" + "\tpop {pc}\n" + "\t.size P256_modinv_sqrt, .-P256_modinv_sqrt\n" + "\t.type P256_times2, %function\n" + "P256_times2:\n" + "\tadds r0,r0\n" + "\tadcs r1,r1,r1\n" + "\tadcs r2,r2,r2\n" + "\tadcs r3,r3,r3\n" + "\tadcs r4,r4,r4\n" + "\tadcs r5,r5,r5\n" + "\tadcs r6,r6,r6\n" + "\tadcs r7,r7,r7\n" + "\tmov r8,#0\n" + "\tadcs r8,r8,r8\n" + "\tsubs r0,#0xffffffff\n" + "\tsbcs r1,r1,#0xffffffff\n" + "\tsbcs r2,r2,#0xffffffff\n" + "\tsbcs r3,r3,#0\n" + "\tsbcs r4,r4,#0\n" + "\tsbcs r5,r5,#0\n" + "\tsbcs r6,r6,#1\n" + "\tsbcs r7,r7,#0xffffffff\n" + "\tsbcs r8,r8,#0\n" + "\tadds r0,r8\n" + "\tadcs r1,r1,r8\n" + "\tadcs r2,r2,r8\n" + "\tadcs r3,r3,#0\n" + "\tadcs r4,r4,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r6,r8, lsr #31\n" + "\tadcs r7,r7,r8\n" + "\tbx lr\n" + "\t.size P256_times2, .-P256_times2\n" + "\t.align 2\n" + "R2_mod_p:\n" + "\t.word 3\n" + "\t.word 0\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffb\n" + "\t.word 0xfffffffe\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffd\n" + "\t.word 4\n" + "\t.type P256_to_montgomery, %function\n" + "P256_to_montgomery:\n" + "\t.global P256_to_montgomery\n" + "\tpush {r0,r4-r11,lr}\n" + "\tadr r2,R2_mod_p\n" + "\tbl P256_mulmod\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_to_montgomery, .-P256_to_montgomery\n" + "\t.type P256_from_montgomery, %function\n" + "P256_from_montgomery:\n" + "\t.global P256_from_montgomery\n" + "\tpush {r0,r4-r11,lr}\n" + "\tmovs r2,#0\n" + "\tmovs r3,#0\n" + "\tpush {r2-r3}\n" + "\tpush {r2-r3}\n" + "\tpush {r2-r3}\n" + "\tmovs r2,#1\n" + "\tpush {r2-r3}\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd sp,#32\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_from_montgomery, .-P256_from_montgomery\n" + "\t.type P256_check_range_p, %function\n" + "P256_check_range_p:\n" + "\t.global P256_check_range_p\n" + "\tpush {r4-r8,lr}\n" + "\tldm r0,{r1-r8}\n" + "\tmovs r0,#0xffffffff\n" + "\tsubs r1,r0\n" + "\tsbcs r2,r2,r0\n" + "\tsbcs r3,r3,r0\n" + "\tsbcs r4,r4,#0\n" + "\tsbcs r5,r5,#0\n" + "\tsbcs r6,r6,#0\n" + "\tsbcs r7,r7,#1\n" + "\tsbcs r8,r8,r0\n" + "\tsbcs r0,r0,r0\n" + "\tlsrs r0,#31\n" + "\tpop {r4-r8,pc}\n" + "\t.size P256_check_range_p, .-P256_check_range_p\n" + "\t.align 2\n" + "P256_order_mu:\n" + "\t.word 0xeedf9bfe\n" + "\t.word 0x012ffd85\n" + "\t.word 0xdf1a6c21\n" + "\t.word 0x43190552\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffe\n" + "\t.word 0xffffffff\n" + "\t.word 0x0\n" + "\t.word 0x1\n" + "\t.type P256_reduce_mod_n_once, %function\n" + "P256_reduce_mod_n_once:\n" + "\tpush {lr}\n" + "\tadr r10,P256_order\n" + "\tldm r10,{r10,r11,r12,lr}\n" + "\tsubs r0,r10\n" + "\tsbcs r1,r1,r11\n" + "\tsbcs r2,r2,r12\n" + "\tsbcs r3,r3,lr\n" + "\tsbcs r4,r4,#0xffffffff\n" + "\tsbcs r5,r5,#0xffffffff\n" + "\tsbcs r6,r6,#0\n" + "\tsbcs r7,r7,#0xffffffff\n" + "\tsbcs r8,r8,#0\n" + "\tsbc r9,r9,r9\n" + "\tand r10,r9\n" + "\tand r11,r9\n" + "\tand r12,r9\n" + "\tand lr,r9\n" + "\tadds r0,r10\n" + "\tadcs r1,r1,r11\n" + "\tadcs r2,r2,r12\n" + "\tadcs r3,r3,lr\n" + "\tadcs r4,r4,r9\n" + "\tadcs r5,r5,r9\n" + "\tadcs r6,r6,#0\n" + "\tadcs r7,r7,r9\n" + "\tadcs r8,r8,#0\n" + "\tpop {pc}\n" + "\t.size P256_reduce_mod_n_once, .-P256_reduce_mod_n_once\n" + "\t.type P256_reduce_mod_n_64bytes, %function\n" + "P256_reduce_mod_n_64bytes:\n" + "\tpush {r0,r4-r11,lr}\n" + "\tsub sp,sp,#108\n" + "\tmov r10,r1\n" + "\tadd r0,sp,#36\n" + "\tadds r1,r1,#28\n" + "\tadr r2,P256_order_mu\n" + "\tbl mul288x288\n" + "\tmov r0,sp\n" + "\tadd r1,sp,#72\n" + "\tadr r2,P256_order\n" + "\tbl mul288x288\n" + "\tldm r10,{r0-r8}\n" + "\tpop {r9-r12}\n" + "\tsubs r0,r0,r9\n" + "\tsbcs r1,r1,r10\n" + "\tsbcs r2,r2,r11\n" + "\tsbcs r3,r3,r12\n" + "\tpop {r9-r12,lr}\n" + "\tsbcs r4,r4,r9\n" + "\tsbcs r5,r5,r10\n" + "\tsbcs r6,r6,r11\n" + "\tsbcs r7,r7,r12\n" + "\tsbcs r8,r8,lr\n" + "\tbl P256_reduce_mod_n_once\n" + "\tbl P256_reduce_mod_n_once\n" + "\tadd sp,sp,#72\n" + "\tpop {r9}\n" + "\tstm r9,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_reduce_mod_n_64bytes, .-P256_reduce_mod_n_64bytes\n" + "\t.type P256_reduce_mod_n_32bytes, %function\n" + "P256_reduce_mod_n_32bytes:\n" + "\t.global P256_reduce_mod_n_32bytes\n" + "\tpush {r0,r4-r11,lr}\n" + "\tldm r1,{r0-r7}\n" + "\tmov r8,#0\n" + "\tbl P256_reduce_mod_n_once\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_reduce_mod_n_32bytes, .-P256_reduce_mod_n_32bytes\n" + "\t.type P256_add_mod_n, %function\n" + "P256_add_mod_n:\n" + "\t.global P256_add_mod_n\n" + "\tpush {r0,r4-r11,lr}\n" + "\tmov r12,r1\n" + "\tldm r2,{r4-r11}\n" + "\tldm r12!,{r0-r3}\n" + "\tadds r0,r4\n" + "\tadcs r1,r1,r5\n" + "\tadcs r2,r2,r6\n" + "\tadcs r3,r3,r7\n" + "\tldm r12,{r4-r7}\n" + "\tadcs r4,r4,r8\n" + "\tadcs r5,r5,r9\n" + "\tadcs r6,r6,r10\n" + "\tadcs r7,r7,r11\n" + "\tmovs r8,#0\n" + "\tadcs r8,r8,r8\n" + "\tbl P256_reduce_mod_n_once\n" + "\tbl P256_reduce_mod_n_once\n" + "\tpop {r8}\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_add_mod_n, .-P256_add_mod_n\n" + "\t.type P256_mul_mod_n, %function\n" + "P256_mul_mod_n:\n" + "\t.global P256_mul_mod_n\n" + "\tmovs r3,#0\n" + "\tpush {r3-r10,lr}\n" + "\tmov r4,r0\n" + "\tldm r1,{r1,r3,r5-r10}\n" + "\tpush {r1,r3,r5-r10}\n" + "\tmovs r1,#0\n" + "\tpush {r1}\n" + "\tldm r2,{r1,r3,r5-r10}\n" + "\tpush {r1,r3,r5-r10}\n" + "\tsub sp,#72\n" + "\tmov r0,sp\n" + "\tadd r1,sp,#72\n" + "\tadd r2,sp,#108\n" + "\tbl mul288x288\n" + "\tmov r0,r4\n" + "\tmov r1,sp\n" + "\tbl P256_reduce_mod_n_64bytes\n" + "\tadd sp,#144\n" + "\tpop {r4-r10,pc}\n" + "\t.size P256_mul_mod_n, .-P256_mul_mod_n\n" + "\t.type P256_divsteps2_31, %function\n" + "P256_divsteps2_31:\n" + "\t.global P256_divsteps2_31\n" + "\tpush {r3,r4-r8,lr}\n" + "\tmovs r4,#1\n" + "\tmovs r5,#0\n" + "\tmovs r6,#0\n" + "\tmovs r7,#1\n" + "\tmov lr,#31\n" + "0:\n" + "\tsubs r3,r0,#1\n" + "\tlsl r12,r2,#31\n" + "\tbic r3,r12,r3\n" + "\tasrs r3,r3,#31\n" + "\tlsr r8,r3,#31\n" + "\teors r0,r0,r3\n" + "\tsubs r0,r0,r3\n" + "\tmul r12,r1,r3\n" + "\tbics r1,r1,r3\n" + "\tumlal r1,r12,r2,r8\n" + "\tumaal r2,r12,r2,r3\n" + "\tmul r12,r4,r3\n" + "\tbics r4,r4,r3\n" + "\tumlal r4,r12,r6,r8\n" + "\tumaal r6,r12,r6,r3\n" + "\tmul r12,r5,r3\n" + "\tbics r5,r5,r3\n" + "\tumlal r5,r12,r7,r8\n" + "\tumaal r7,r12,r7,r3\n" + "\tands r12,r2,#1\n" + "\tadds r0,r0,#1\n" + "\tmul r3,r12,r1\n" + "\tadds r2,r2,r3\n" + "\tlsrs r2,r2,#1\n" + "\tumlal r6,r8,r12,r4\n" + "\tumlal r7,r8,r12,r5\n" + "\tadds r4,r4,r4\n" + "\tadds r5,r5,r5\n" + "\tsubs lr,lr,#1\n" + "\tbne 0b\n" + "\tpop {r3}\n" + "\tstm r3!,{r4-r7}\n" + "\tpop {r4-r8,pc}\n" + "\t.size P256_divsteps2_31, .-P256_divsteps2_31\n" + "\t.type P256_matrix_mul_fg_9, %function\n" + "P256_matrix_mul_fg_9:\n" + "\t.global P256_matrix_mul_fg_9\n" + "\tpush {r4-r11,lr}\n" + "\tand r4,r0,r0,lsl #1\n" + "\tasrs r4,r4,#31\n" + "\teors r0,r0,r4\n" + "\tsubs r0,r0,r4\n" + "\tand r5,r1,r1,lsl #1\n" + "\tasrs r5,r5,#31\n" + "\teors r1,r1,r5\n" + "\tsubs r1,r1,r5\n" + "\tldm r2!,{r6}\n" + "\tldr r7,[r2,#36]\n" + "\teors r4,r4,r6\n" + "\teors r5,r5,r7\n" + "\teors r4,r4,r5\n" + "\tstm r3!,{r5}\n" + "\tpush {r1,r2,r3}\n" + "\tldm r2!,{r1,r3,r5-r11}\n" + "\teors r1,r1,r4\n" + "\teors r3,r3,r4\n" + "\teors r5,r5,r4\n" + "\teors r6,r6,r4\n" + "\teors r7,r7,r4\n" + "\teor r8,r8,r4\n" + "\teor r9,r9,r4\n" + "\teor r10,r10,r4\n" + "\tsubs r1,r1,r4\n" + "\tsbcs r3,r3,r4\n" + "\tsbcs r5,r5,r4\n" + "\tsbcs r6,r6,r4\n" + "\tsbcs r7,r7,r4\n" + "\tsbcs r8,r8,r4\n" + "\tsbcs r9,r9,r4\n" + "\tsbcs r10,r10,r4\n" + "\teor r4,r4,r11\n" + "\tumull r1,lr,r0,r1\n" + "\tmovs r2,#0\n" + "\tumull r11,r12,r2,r2\n" + "\tumaal r2,lr,r0,r3\n" + "\tumaal r11,lr,r0,r5\n" + "\tumull r3,r5,r12,r12\n" + "\tumaal r3,lr,r0,r6\n" + "\tumaal r5,lr,r0,r7\n" + "\tumull r6,r7,r12,r12\n" + "\tumaal r6,lr,r0,r8\n" + "\tumaal r7,lr,r0,r9\n" + "\tumaal r12,lr,r0,r10\n" + "\tmla lr,r0,r4,lr\n" + "\tpop {r0,r4}\n" + "\tadds r4,r4,#40\n" + "\tldm r4!,{r8,r9}\n" + "\tmov r10,#0\n" + "\tumaal r1,r10,r0,r8\n" + "\tumaal r2,r10,r0,r9\n" + "\tadds r1,r1,r1\n" + "\tadcs r2,r2,r2\n" + "\tldm r4!,{r1,r8,r9}\n" + "\tumaal r10,r11,r0,r1\n" + "\tumaal r11,r3,r0,r8\n" + "\tumaal r3,r5,r0,r9\n" + "\tadcs r10,r10,r10\n" + "\tadcs r11,r11,r11\n" + "\tadcs r3,r3,r3\n" + "\tldm r4,{r1,r4,r8,r9}\n" + "\tumaal r5,r6,r0,r1\n" + "\tumaal r6,r7,r0,r4\n" + "\tumaal r7,r12,r0,r8\n" + "\tumaal r12,lr,r0,r9\n" + "\tadcs r5,r5,r5\n" + "\tadcs r6,r6,r6\n" + "\tadcs r7,r7,r7\n" + "\tadcs r12,r12,r12\n" + "\tsbcs lr,lr,lr\n" + "\tmvn lr,lr\n" + "\tpop {r1}\n" + "\tstm r1!,{r2,r10,r11}\n" + "\tstm r1!,{r3,r5,r6,r7,r12,lr}\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_matrix_mul_fg_9, .-P256_matrix_mul_fg_9\n" + "\t.align 2\n" + "\t.type P256_matrix_mul_mod_n, %function\n" + "P256_matrix_mul_mod_n:\n" + "\t.global P256_matrix_mul_mod_n\n" + "\tpush {r4-r11,lr}\n" + "\tand r4,r0,r0,lsl #1\n" + "\tasrs r4,r4,#31\n" + "\teors r0,r0,r4\n" + "\tsubs r0,r0,r4\n" + "\tand r5,r1,r1,lsl #1\n" + "\tasrs r5,r5,#31\n" + "\teors r1,r1,r5\n" + "\tsubs r1,r1,r5\n" + "\tldm r2!,{r6}\n" + "\tldr r7,[r2,#32]\n" + "\teors r4,r4,r6\n" + "\teors r5,r5,r7\n" + "\teors r4,r4,r5\n" + "\tstm r3!,{r5}\n" + "\tpush {r1,r2,r3}\n" + "\tldm r2,{r1-r3,r5-r9}\n" + "\teors r1,r1,r4\n" + "\teors r2,r2,r4\n" + "\teors r3,r3,r4\n" + "\teors r5,r5,r4\n" + "\teors r6,r6,r4\n" + "\teors r7,r7,r4\n" + "\teor r8,r8,r4\n" + "\teor r9,r9,r4\n" + "\tsubs r1,r1,r4\n" + "\tsbcs r2,r2,r4\n" + "\tsbcs r3,r3,r4\n" + "\tsbcs r5,r5,r4\n" + "\tsbcs r6,r6,r4\n" + "\tsbcs r7,r7,r4\n" + "\tsbcs r8,r8,r4\n" + "\tsbcs r9,r9,r4\n" + "\tsbcs r4,r4,r4\n" + "\tlsrs lr,r4,#31\n" + "\tmov r12,#0\n" + "\tldrd r10,r11,P256_order_local\n" + "\tumaal r1,r12,lr,r10\n" + "\tumaal r2,r12,lr,r11\n" + "\tldrd r10,r11,P256_order_local+8\n" + "\tumaal r3,r12,lr,r10\n" + "\tumaal r5,r12,lr,r11\n" + "\tumaal r6,r12,lr,r4\n" + "\tumaal r7,r12,lr,r4\n" + "\tmov r10,#0\n" + "\tumaal r8,r12,lr,r10\n" + "\tumaal r9,r12,lr,r4\n" + "\tumull r11,lr,r10,r10\n" + "\tumull r10,lr,r0,r1\n" + "\tumull r1,r12,r11,r11\n" + "\tumaal r11,lr,r0,r2\n" + "\tumaal r1,lr,r0,r3\n" + "\tumull r2,r3,r12,r12\n" + "\tumaal r2,lr,r0,r5\n" + "\tumaal r3,lr,r0,r6\n" + "\tumull r4,r5,r12,r12\n" + "\tumaal r4,lr,r0,r7\n" + "\tumaal r5,lr,r0,r8\n" + "\tumaal r12,lr,r0,r9\n" + "\tpop {r0,r6}\n" + "\tadds r6,r6,#36\n" + "\tldm r6!,{r8,r9}\n" + "\tmovs r7,#0\n" + "\tumaal r10,r7,r0,r8\n" + "\tumaal r11,r7,r0,r9\n" + "\tldm r6!,{r8,r9}\n" + "\tumaal r1,r7,r0,r8\n" + "\tumaal r2,r7,r0,r9\n" + "\tldm r6!,{r8,r9}\n" + "\tumaal r3,r7,r0,r8\n" + "\tumaal r4,r7,r0,r9\n" + "\tldm r6!,{r8,r9}\n" + "\tumaal r5,r7,r0,r8\n" + "\tumaal r12,r7,r0,r9\n" + "\tadd lr,lr,r7\n" + "\tldr r0,=0xee00bc4f\n" + "\tmul r0,r10,r0\n" + "\tmovs r6,#0\n" + "\tldrd r8,r9,P256_order_local\n" + "\tumaal r10,r6,r0,r8\n" + "\tumaal r11,r6,r0,r9\n" + "\tsubs r11,r11,r8\n" + "\tldrd r8,r10,P256_order_local+8\n" + "\tumaal r1,r6,r0,r8\n" + "\tsbcs r1,r1,r9\n" + "\tumaal r2,r6,r0,r10\n" + "\tmov r9,#-1\n" + "\tumaal r3,r6,r0,r9\n" + "\tumaal r4,r6,r0,r9\n" + "\tmovs r7,#0\n" + "\tumaal r5,r6,r0,r7\n" + "\tumaal r12,r6,r0,r9\n" + "\tumaal lr,r6,r7,r7\n" + "\tsbcs r2,r2,r8\n" + "\tsbcs r3,r3,r10\n" + "\tsbcs r4,r4,r9\n" + "\tsbcs r5,r5,r9\n" + "\tsbcs r12,r12,r7\n" + "\tsbcs lr,lr,r9\n" + "\tsbcs r6,r6,r7\n" + "\tldrd r0,r9,P256_order_local\n" + "\tlsrs r6,r6,#31\n" + "\tumaal r7,r11,r6,r0\n" + "\tumaal r1,r11,r6,r9\n" + "\tumaal r2,r11,r6,r8\n" + "\tumaal r3,r11,r6,r10\n" + "\trsbs r0,r6,#0\n" + "\tumaal r4,r11,r6,r0\n" + "\tumaal r5,r11,r6,r0\n" + "\tmov r8,#0\n" + "\tumaal r11,r12,r6,r8\n" + "\tumaal r12,lr,r6,r0\n" + "\tpop {r6}\n" + "\tstm r6!,{r7}\n" + "\tstm r6!,{r1,r2,r3,r4,r5,r11,r12}\n" + "\tpop {r4-r11,pc}\n" + "\t.ltorg\n" + "\t.size P256_matrix_mul_mod_n, .-P256_matrix_mul_mod_n\n" + "\t.align 2\n" + "P256_order_local:\n" + "\t.type P256_order, %object\n" + "P256_order:\n" + "\t.global P256_order\n" + "\t.word 0xFC632551\n" + "\t.word 0xF3B9CAC2\n" + "\t.word 0xA7179E84\n" + "\t.word 0xBCE6FAAD\n" + "\t.word 0xFFFFFFFF\n" + "\t.word 0xFFFFFFFF\n" + "\t.word 0\n" + "\t.word 0xFFFFFFFF\n" + "\t.word 0\n" + "\t.size P256_order, .-P256_order\n" + "\t.type P256_check_range_n, %function\n" + "P256_check_range_n:\n" + "\t.global P256_check_range_n\n" + "\tpush {r4-r11,lr}\n" + "\tldm r0,{r1-r8}\n" + "\torrs r0,r1,r2\n" + "\torrs r0,r3\n" + "\torrs r0,r4\n" + "\torrs r0,r5\n" + "\torrs r0,r6\n" + "\torrs r0,r7\n" + "\torrs r0,r8\n" + "\tbeq 0f\n" + "\tadr r0,P256_order\n" + "\tldm r0!,{r9-r12}\n" + "\tsubs r1,r9\n" + "\tsbcs r2,r2,r10\n" + "\tsbcs r3,r3,r11\n" + "\tsbcs r4,r4,r12\n" + "\tldm r0,{r0-r3}\n" + "\tsbcs r5,r5,r0\n" + "\tsbcs r6,r6,r1\n" + "\tsbcs r7,r7,r2\n" + "\tsbcs r8,r8,r3\n" + "\tsbcs r0,r0,r0\n" + "\tlsrs r0,#31\n" + "0:\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_check_range_n, .-P256_check_range_n\n" + "\t.align 2\n" + "b_mont:\n" + "\t.word 0x29c4bddf\n" + "\t.word 0xd89cdf62\n" + "\t.word 0x78843090\n" + "\t.word 0xacf005cd\n" + "\t.word 0xf7212ed6\n" + "\t.word 0xe5a220ab\n" + "\t.word 0x04874834\n" + "\t.word 0xdc30061d\n" + "three_mont:\n" + "\t.word 0x3\n" + "\t.word 0x0\n" + "\t.word 0x0\n" + "\t.word 0xfffffffd\n" + "\t.word 0xffffffff\n" + "\t.word 0xffffffff\n" + "\t.word 0xfffffffc\n" + "\t.word 0x2\n" + "\t.type P256_point_is_on_curve, %function\n" + "P256_point_is_on_curve:\n" + "\t.global P256_point_is_on_curve\n" + "\tpush {r0,r4-r11,lr}\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r0,[sp,#32]\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tmov r1,sp\n" + "\tadr r2,three_mont\n" + "\tbl P256_submod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#64]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_submod\n" + "\tadr r8,b_mont\n" + "\tldm r8!,{r9-r12}\n" + "\teors r0,r9\n" + "\tittt eq\n" + "\teorseq r1,r10\n" + "\teorseq r2,r11\n" + "\teorseq r3,r12\n" + "\tldm r8,{r9-r12}\n" + "\titttt eq\n" + "\teorseq r4,r9\n" + "\teorseq r5,r10\n" + "\teorseq r6,r11\n" + "\teorseq r7,r12\n" + "\tmov r0,#0\n" + "\tit eq\n" + "\tmoveq r0,#1\n" + "\tadd sp,#68\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_point_is_on_curve, .-P256_point_is_on_curve\n" + "\t.align 2\n" + "P256_p:\n" + "\t.word 0xffffffff\n" + "\t.word 0xffffffff\n" + "\t.word 0xffffffff\n" + "\t.word 0\n" + "\t.word 0\n" + "\t.word 0\n" + "\t.word 1\n" + "\t.word 0xffffffff\n" + "\t.type P256_jacobian_to_affine, %function\n" + "P256_jacobian_to_affine:\n" + "\t.global P256_jacobian_to_affine\n" + "\tpush {r0,r1,r2,r4-r11,lr}\n" + "\tadds r2,#64\n" + "\tldm r2,{r0-r7}\n" + "\tmov r8,#0\n" + "\tbl P256_modinv_sqrt\n" + "\tpush {r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmov r1,sp\n" + "\tldr r2,[sp,#72]\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#64]\n" + "\tstm r8,{r0-r7}\n" + "\tldr r2,[sp,#72]\n" + "\tadd r1,sp,#32\n" + "\tadds r2,r2,#32\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#68]\n" + "\tstm r8,{r0-r7}\n" + "\tadd sp,#76\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_jacobian_to_affine, .-P256_jacobian_to_affine\n" + "\t.type P256_double_j, %function\n" + "P256_double_j:\n" + "\t.global P256_double_j\n" + "\tpush {r0,r1,r4-r11,lr}\n" + "\tadds r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#36]\n" + "\tadds r1,#32\n" + "\tadd r2,r1,#32\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#32]\n" + "\tadd r8,#64\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#36]\n" + "\tmov r2,sp\n" + "\tbl P256_addmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#68]\n" + "\tadd r2,sp,#32\n" + "\tbl P256_submod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tlsl r8,r0,#31\n" + "\tadds r0,r0,r8, asr #31\n" + "\tadcs r1,r1,r8, asr #31\n" + "\tadcs r2,r2,r8, asr #31\n" + "\tadcs r3,r3,#0\n" + "\tadcs r4,r4,#0\n" + "\tadcs r5,r5,#0\n" + "\tadcs r6,r6,r8, lsr #31\n" + "\tadcs r7,r7,r8, asr #31\n" + "\trrxs r7,r7\n" + "\trrxs r6,r6\n" + "\trrxs r5,r5\n" + "\trrxs r4,r4\n" + "\trrxs r3,r3\n" + "\trrxs r2,r2\n" + "\trrxs r1,r1\n" + "\trrx r0,r0\n" + "\tstm sp,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_addmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r0,[sp,#68]\n" + "\tadds r0,#32\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tldr r8,[sp,#64]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldrd r0,r1,[sp,#96]\n" + "\tadd r2,r0,#32\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_times2\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tmov r2,r8\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tmov r2,r8\n" + "\tadd r1,r2,#32\n" + "\tbl P256_submod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#64\n" + "\tadd r2,sp,#32\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#64\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#64\n" + "\tmov r2,sp\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd sp,#104\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_double_j, .-P256_double_j\n" + "\t.type add_sub_helper, %function\n" + "add_sub_helper:\n" + "\tpush {lr}\n" + "\tldm r1!,{r5-r12}\n" + "\tstm r0!,{r5-r12}\n" + "\tldm r1!,{r5-r12}\n" + "\tcbz r2,0f\n" + "\tmov lr,#0\n" + "\trsbs r4,r2,#0\n" + "\tsubs r5,r4,r5\n" + "\tsbcs r6,r4,r6\n" + "\tsbcs r7,r4,r7\n" + "\tsbcs r8,lr,r8\n" + "\tsbcs r9,lr,r9\n" + "\tsbcs r10,lr,r10\n" + "\tsbcs r11,r2,r11\n" + "\tsbcs r12,r4,r12\n" + "0:\n" + "\tstm r0!,{r5-r12}\n" + "\tcbnz r3,1f\n" + "\tldm r1,{r5-r12}\n" + "\tstm r0,{r5-r12}\n" + "\tb 2f\n" + "1:\n" + "\tmovs r4,#0\n" + "\tumull r5,r10,r4,r4\n" + "\tmvns r6,r4\n" + "\tmvns r7,r4\n" + "\tmov r8,#0xffffffff\n" + "\tmov r9,#0xfffffffe\n" + "\tstm r0,{r3-r10}\n" + "2:\n" + "\tpop {pc}\n" + "\t.size add_sub_helper, .-add_sub_helper\n" + "\t.type P256_add_sub_j, %function\n" + "P256_add_sub_j:\n" + "\t.global P256_add_sub_j\n" + "\tpush {r0-r11,lr}\n" + "\tadd r4,r0,#64\n" + "\tldm r4,{r4-r11}\n" + "\torrs r4,r5\n" + "\torrs r4,r6\n" + "\torrs r4,r7\n" + "\torrs r4,r8\n" + "\torrs r4,r9\n" + "\torrs r4,r10\n" + "\torrs r4,r11\n" + "\tbne 2f\n" + "\tbl add_sub_helper\n" + "\tadd sp,#16\n" + "\tpop {r4-r11,pc}\n" + "2:\n" + "\tcbnz r3,100f\n" + "\tadds r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#32]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#32]\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#36]\n" + "\tadds r1,#64\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#32]\n" + "\tadds r1,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#32]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tb 101f\n" + "100:\n" + "\tsub sp,#32\n" + "101:\n" + "\tldr r1,[sp,#32]\n" + "\tadds r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r1,[sp,#68]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#64]\n" + "\tadds r1,#64\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#68]\n" + "\tadds r1,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#64]\n" + "\tadd r2,sp,#32\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#64]\n" + "\tstm r8,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\tldr r2,[sp,#96]\n" + "\tadd r1,r2,#64\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#64\n" + "\tstm r8,{r0-r7}\n" + "\tldr r1,[sp,#108]\n" + "\tcbnz r1,102f\n" + "\tldr r1,[sp,#100]\n" + "\tadds r1,#64\n" + "\tmov r2,r8\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#64\n" + "\tstm r8,{r0-r7}\n" + "102:\n" + "\tldr r1,[sp,#96]\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\torrs r1,r0\n" + "\torrs r1,r2\n" + "\torrs r1,r3\n" + "\torrs r1,r4\n" + "\torrs r1,r5\n" + "\torrs r1,r6\n" + "\torrs r0,r1,r7\n" + "3:\n" + "\tpush {r0}\n" + "\tldr r1,[sp,#100]\n" + "\tadds r1,#32\n" + "\tadd r2,sp,#36\n" + "\tldr r3,[sp,#108]\n" + "\tcbz r3,4f\n" + "\tbl P256_addmod\n" + "\tb 5f\n" + "4:\n" + "\tbl P256_submod\n" + "5:\n" + "\tldr r8,[sp,#100]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tpop {r8}\n" + "\torrs r1,r0\n" + "\torrs r1,r2\n" + "\torrs r1,r3\n" + "\torrs r1,r4\n" + "\torrs r1,r5\n" + "\torrs r1,r6\n" + "\torrs r1,r7\n" + "\torrs r1,r8\n" + "\tbne 6f\n" + "\tadd sp,#96\n" + "\tldm sp,{r0-r3}\n" + "\tbl add_sub_helper\n" + "\tldr r0,[sp,#0]\n" + "\tmov r1,r0\n" + "\tadd sp,#16\n" + "\tbl P256_double_j\n" + "\tpop {r4-r11,pc}\n" + "6:\n" + "\tadd r1,sp,#64\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#64\n" + "\tstm r8,{r0-r7}\n" + "\tldr r0,[sp,#96]\n" + "\tadds r0,#32\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tstm sp,{r0-r7}\n" + "\tadd r1,sp,#32\n" + "\tldr r2,[sp,#96]\n" + "\tbl P256_mulmod\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmov r1,sp\n" + "\tldr r2,[sp,#96]\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tadd r0,sp,#64\n" + "\tldm r0,{r0-r7}\n" + "\tbl P256_times2\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#96]\n" + "\tmov r2,sp\n" + "\tbl P256_submod\n" + "\tldr r8,[sp,#96]\n" + "\tstm r8,{r0-r7}\n" + "\tadd r1,sp,#64\n" + "\tldr r2,[sp,#96]\n" + "\tbl P256_submod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r1,[sp,#96]\n" + "\tadds r1,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tstm sp,{r0-r7}\n" + "\tldr r0,[sp,#104]\n" + "\tmov r1,sp\n" + "\tadd r2,sp,#32\n" + "\tcbz r0,7f\n" + "\tbl P256_addmod\n" + "\tb 8f\n" + "7:\n" + "\tbl P256_submod\n" + "8:\n" + "\tldr r8,[sp,#96]\n" + "\tadd r8,#32\n" + "\tstm r8,{r0-r7}\n" + "\tadd sp,#112\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_add_sub_j, .-P256_add_sub_j\n" + "\t.type P256_verify_last_step, %function\n" + "P256_verify_last_step:\n" + "\t.global P256_verify_last_step\n" + "\tpush {r0,r1,r4-r11,lr}\n" + "\tsub sp,#32\n" + "\tadd r1,#64\n" + "\tldm r1,{r0-r7}\n" + "\tbl P256_sqrmod\n" + "\tpush {r0-r7}\n" + "\torrs r0,r1\n" + "\torrs r0,r2\n" + "\torrs r0,r3\n" + "\torrs r0,r4\n" + "\torrs r0,r5\n" + "\torrs r0,r6\n" + "\torrs r0,r7\n" + "\tbeq 0f\n" + "\tldr r1,[sp,#64]\n" + "2:\n" + "\tadd r0,sp,#32\n" + "\tbl P256_to_montgomery\n" + "\tadd r1,sp,#32\n" + "\tmov r2,sp\n" + "\tbl P256_mulmod\n" + "\tldr r8,[sp,#68]\n" + "\tldm r8!,{r9-r12}\n" + "\teors r0,r9\n" + "\tittt eq\n" + "\teorseq r1,r10\n" + "\teorseq r2,r11\n" + "\teorseq r3,r12\n" + "\tldm r8!,{r9-r12}\n" + "\titttt eq\n" + "\teorseq r4,r9\n" + "\teorseq r5,r10\n" + "\teorseq r6,r11\n" + "\teorseq r7,r12\n" + "\tmov r0,#1\n" + "\tbeq 1f\n" + "\tadr r0,P256_order\n" + "\tldm r0,{r8-r11}\n" + "\tldr r0,[sp,#64]\n" + "\tcbz r0,0f\n" + "\tldm r0,{r0-r7}\n" + "\tadds r0,r8\n" + "\tadcs r1,r1,r9\n" + "\tadcs r2,r2,r10\n" + "\tadcs r3,r3,r11\n" + "\tadcs r4,r4,#0xffffffff\n" + "\tadcs r5,r5,#0xffffffff\n" + "\tadcs r6,r6,#0\n" + "\tadcs r7,r7,#0xffffffff\n" + "\tbcs 0f\n" + "\tsubs r8,r0,#0xffffffff\n" + "\tsbcs r8,r1,#0xffffffff\n" + "\tsbcs r8,r2,#0xffffffff\n" + "\tsbcs r8,r3,#0\n" + "\tsbcs r8,r4,#0\n" + "\tsbcs r8,r5,#0\n" + "\tsbcs r8,r6,#1\n" + "\tsbcs r8,r7,#0xffffffff\n" + "\tbcs 0f\n" + "\tadd r8,sp,#32\n" + "\tstm r8,{r0-r7}\n" + "\tmovs r2,#0\n" + "\tstr r2,[sp,#64]\n" + "\tmov r1,r8\n" + "\tb 2b\n" + "0:\n" + "\tmovs r0,#0\n" + "1:\n" + "\tadd sp,#72\n" + "\tpop {r4-r11,pc}\n" + "\t.size P256_verify_last_step, .-P256_verify_last_step\n" + "\t.type P256_negate_mod_m_if, %function\n" + "P256_negate_mod_m_if:\n" + "\tpush {r4-r8,lr}\n" + "\trsb r8,r2,#1\n" + "\tmovs r6,#8\n" + "\tsubs r7,r7\n" + "0:\n" + "\tldm r1!,{r4,r12}\n" + "\tldm r3!,{r5,lr}\n" + "\tsbcs r5,r5,r4\n" + "\tumull r4,r7,r8,r4\n" + "\tumaal r4,r7,r2,r5\n" + "\tsbcs lr,lr,r12\n" + "\tumull r12,r7,r8,r12\n" + "\tumaal r12,r7,r2,lr\n" + "\tstm r0!,{r4,r12}\n" + "\tsub r6,#2\n" + "\tcbz r6,1f\n" + "\tb 0b\n" + "1:\n" + "\tpop {r4-r8,pc}\n" + "\t.size P256_negate_mod_m_if, .-P256_negate_mod_m_if\n" + "\t.type P256_negate_mod_n_if, %function\n" + "P256_negate_mod_n_if:\n" + "\t.global P256_negate_mod_n_if\n" + "\tldr r3,=P256_order\n" + "\tb P256_negate_mod_m_if\n" + "\t.size P256_negate_mod_n_if, .-P256_negate_mod_n_if\n" + "\t.type P256_negate_mod_p_if, %function\n" + "P256_negate_mod_p_if:\n" + "\t.global P256_negate_mod_p_if\n" + "\tadr r3,P256_p\n" + "\tb P256_negate_mod_m_if\n" + "\t.size P256_negate_mod_p_if, .-P256_negate_mod_p_if\n" + "\t.align 2\n" + ".popsection\n"); // No .end here: it would drop the C code that follows + +// mg_p256_*() wrappers, see tls_p256.h. The API uses big endian byte strings, +// this library uses little endian 32-bit words, so convert both ways + +int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]) { + uint32_t x[8], y[8], d[8]; + int tries, ok = 0; + for (tries = 0; tries < 64 && !ok; tries++) { + if (!mg_random(d, sizeof(d))) break; + ok = p256_keygen(x, y, d); + } + if (ok) { + p256_convert_endianness(public_key, x, 32); + p256_convert_endianness(public_key + 32, y, 32); + p256_convert_endianness(private_key, d, 32); + } + mg_bzero((unsigned char *) d, sizeof(d)); + return ok; +} + +int mg_p256_compute_public_key(const uint8_t private_key[32], + uint8_t public_key[64]) { + uint32_t x[8], y[8], d[8]; + int ok; + p256_convert_endianness(d, private_key, 32); + ok = p256_keygen(x, y, d); // False if the key is out of range + if (ok) { + p256_convert_endianness(public_key, x, 32); + p256_convert_endianness(public_key + 32, y, 32); + } + mg_bzero((unsigned char *) d, sizeof(d)); + return ok; +} + +int mg_p256_shared_secret(const uint8_t public_key[64], + const uint8_t private_key[32], uint8_t secret[32]) { + uint32_t x[8], y[8], d[8]; + int ok; + p256_convert_endianness(x, public_key, 32); + p256_convert_endianness(y, public_key + 32, 32); + p256_convert_endianness(d, private_key, 32); + ok = p256_ecdh_calc_shared_secret(secret, d, x, y); // Validates the point + mg_bzero((unsigned char *) d, sizeof(d)); + return ok; +} + +int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash, + size_t hash_size, const uint8_t k[32], + uint8_t signature[64]) { + uint32_t r[8], s[8], d[8], kw[8]; + int ok; + p256_convert_endianness(d, private_key, 32); + p256_convert_endianness(kw, k, 32); + ok = p256_sign(r, s, hash, (uint32_t) hash_size, d, kw); // False if k is + // out of range + if (ok) { + p256_convert_endianness(signature, r, 32); + p256_convert_endianness(signature + 32, s, 32); + } + mg_bzero((unsigned char *) d, sizeof(d)); + mg_bzero((unsigned char *) kw, sizeof(kw)); + return ok; +} + +int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash, + size_t hash_size, const uint8_t signature[64]) { + uint32_t x[8], y[8], r[8], s[8]; + p256_convert_endianness(x, public_key, 32); + p256_convert_endianness(y, public_key + 32, 32); + p256_convert_endianness(r, signature, 32); + p256_convert_endianness(s, signature + 32, 32); + return p256_verify(x, y, hash, (uint32_t) hash_size, r, s); // Checks point +} +#undef get_bit +#endif // MG_P256 == MG_P256_LENNGREN diff --git a/src/tls_p384.c b/src/tls_p384.c new file mode 100644 index 00000000..d80c2ead --- /dev/null +++ b/src/tls_p384.c @@ -0,0 +1,834 @@ +// P-384 from BearSSL, https://bearssl.org commit 7bea48e +// P-384 signature verification, used by built-in TLS for certificates and +// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c +// +// Copyright (c) 2016 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static +// functions with p384_. Trimmed to P-384. Structs are not typedef-ed + +#include "tls_p384.h" +#include "tls_p256.h" + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) + +// From BearSSL src/ec/ec_secp384r1.c +static const unsigned char P384_N[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, + 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, + 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 +}; + +static const unsigned char P384_G[] = { + 0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, + 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, + 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, + 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, + 0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, + 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, + 0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, + 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, + 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, + 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, + 0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, + 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, + 0x5F +}; + + +// From BearSSL src/ec/ec_prime_i31.c +/* + * Parameters for supported curves (field modulus, and 'b' equation + * parameter; both values use the 'i31' format, and 'b' is in Montgomery + * representation). + */ + +static const uint32_t P384_P[] = { + 0x0000018C, + 0x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8, + 0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, + 0x00000FFF +}; + +static const uint32_t P384_R2[] = { + 0x0000018C, + 0x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF, + 0x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF, + 0x00008000, 0x00008000, 0x00000000, 0x00000000, + 0x00000000 +}; + +static const uint32_t P384_B[] = { + 0x0000018C, + 0x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C, + 0x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B, + 0x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE, + 0x000008A5 +}; + +struct p384_curve_params { + const uint32_t *p; + const uint32_t *b; + const uint32_t *R2; + uint32_t p0i; + size_t point_len; +}; + +static inline const struct p384_curve_params * +p384_id_to_curve(int curve) +{ + static const struct p384_curve_params pp = { + P384_P, P384_B, P384_R2, 0x00000001, 97 + }; + + (void)curve; + return &pp; +} + +#define I31_LEN ((MG_BR_MAX_EC_SIZE + 61) / 31) + +/* + * Type for a point in Jacobian coordinates: + * -- three values, x, y and z, in Montgomery representation + * -- affine coordinates are X = x / z^2 and Y = y / z^3 + * -- for the point at infinity, z = 0 + */ +struct p384_jacobian { + uint32_t c[3][I31_LEN]; +}; + +/* + * We use a custom interpreter that uses a dozen registers, and + * only six operations: + * MSET(d, a) copy a into d + * MADD(d, a) d = d+a (modular) + * MSUB(d, a) d = d-a (modular) + * MMUL(d, a, b) d = a*b (Montgomery multiplication) + * MINV(d, a, b) invert d modulo p; a and b are used as scratch registers + * MTZ(d) clear return value if d = 0 + * Destination of MMUL (d) must be distinct from operands (a and b). + * There is no such constraint for MSUB and MADD. + * + * Registers include the operand coordinates, and temporaries. + */ +#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4)) +#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4)) +#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4)) +#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b)) +#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b)) +#define MTZ(d) (0x5000 + ((d) << 8)) +#define ENDCODE 0 + +/* + * Registers for the input operands. + */ +#define P1x 0 +#define P1y 1 +#define P1z 2 +#define P2x 3 +#define P2y 4 +#define P2z 5 + +/* + * Alternate names for the first input operand. + */ +#define Px 0 +#define Py 1 +#define Pz 2 + +/* + * Temporaries. + */ +#define t1 6 +#define t2 7 +#define t3 8 +#define t4 9 +#define t5 10 +#define t6 11 +#define t7 12 + +/* + * Extra scratch registers available when there is no second operand (e.g. + * for "double" and "affine"). + */ +#define t8 3 +#define t9 4 +#define t10 5 + +/* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * If y = 0 (P has order 2) then this yields infinity (z' = 0), as it + * should. This case should not happen anyway, because our curves have + * prime order, and thus do not contain any point of order 2. + * + * If P is infinity (z = 0), then again the formulas yield infinity, + * which is correct. Thus, this code works for all points. + * + * Cost: 8 multiplications + */ +static const uint16_t p384_code_double[] = { + /* + * Compute z^2 (in t1). + */ + MMUL(t1, Pz, Pz), + + /* + * Compute x-z^2 (in t2) and then x+z^2 (in t1). + */ + MSET(t2, Px), + MSUB(t2, t1), + MADD(t1, Px), + + /* + * Compute m = 3*(x+z^2)*(x-z^2) (in t1). + */ + MMUL(t3, t1, t2), + MSET(t1, t3), + MADD(t1, t3), + MADD(t1, t3), + + /* + * Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + MMUL(t3, Py, Py), + MADD(t3, t3), + MMUL(t2, Px, t3), + MADD(t2, t2), + + /* + * Compute x' = m^2 - 2*s. + */ + MMUL(Px, t1, t1), + MSUB(Px, t2), + MSUB(Px, t2), + + /* + * Compute z' = 2*y*z. + */ + MMUL(t4, Py, Pz), + MSET(Pz, t4), + MADD(Pz, t4), + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + MSUB(t2, Px), + MMUL(Py, t1, t2), + MMUL(t4, t3, t3), + MSUB(Py, t4), + MSUB(Py, t4), + + ENDCODE +}; + +/* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + * + * If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that + * z3 == 0, so the result is correct. + * If either of P1 or P2 is infinity, but not both, then z3 == 0, which is + * not correct. + * h == 0 only if u1 == u2; this happens in two cases: + * -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2 + * -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity) + * + * Thus, the following situations are not handled correctly: + * -- P1 = 0 and P2 != 0 + * -- P1 != 0 and P2 = 0 + * -- P1 = P2 + * All other cases are properly computed. However, even in "incorrect" + * situations, the three coordinates still are properly formed field + * elements. + * + * The returned flag is cleared if r == 0. This happens in the following + * cases: + * -- Both points are on the same horizontal line (same Y coordinate). + * -- Both points are infinity. + * -- One point is infinity and the other is on line Y = 0. + * The third case cannot happen with our curves (there is no valid point + * on line Y = 0 since that would be a point of order 2). If the two + * source points are non-infinity, then remains only the case where the + * two points are on the same horizontal line. + * + * This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and + * P2 != 0: + * -- If the returned value is not the point at infinity, then it was properly + * computed. + * -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result + * is indeed the point at infinity. + * -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should + * use the 'double' code. + * + * Cost: 16 multiplications + */ +static const uint16_t p384_code_add[] = { + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + MMUL(t3, P2z, P2z), + MMUL(t1, P1x, t3), + MMUL(t4, P2z, t3), + MMUL(t3, P1y, t4), + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + MMUL(t4, P1z, P1z), + MMUL(t2, P2x, t4), + MMUL(t5, P1z, t4), + MMUL(t4, P2y, t5), + + /* + * Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4). + */ + MSUB(t2, t1), + MSUB(t4, t3), + + /* + * Report cases where r = 0 through the returned flag. + */ + MTZ(t4), + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5). + */ + MMUL(t7, t2, t2), + MMUL(t6, t1, t7), + MMUL(t5, t7, t2), + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + * t1 and t7 can be used as scratch registers. + */ + MMUL(P1x, t4, t4), + MSUB(P1x, t5), + MSUB(P1x, t6), + MSUB(P1x, t6), + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + MSUB(t6, P1x), + MMUL(P1y, t4, t6), + MMUL(t1, t5, t3), + MSUB(P1y, t1), + + /* + * Compute z3 = h*z1*z2. + */ + MMUL(t1, P1z, P2z), + MMUL(P1z, t1, t2), + + ENDCODE +}; + +/* + * Check that the point is on the curve. This code snippet assumes the + * following conventions: + * -- Coordinates x and y have been freshly decoded in P1 (but not + * converted to Montgomery coordinates yet). + * -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1. + */ +static const uint16_t p384_code_check[] = { + + /* Convert x and y to Montgomery representation. */ + MMUL(t1, P1x, P2x), + MMUL(t2, P1y, P2x), + MSET(P1x, t1), + MSET(P1y, t2), + + /* Compute x^3 in t1. */ + MMUL(t2, P1x, P1x), + MMUL(t1, P1x, t2), + + /* Subtract 3*x from t1. */ + MSUB(t1, P1x), + MSUB(t1, P1x), + MSUB(t1, P1x), + + /* Add b. */ + MADD(t1, P2y), + + /* Compute y^2 in t2. */ + MMUL(t2, P1y, P1y), + + /* Compare y^2 with x^3 - 3*x + b; they must match. */ + MSUB(t1, t2), + MTZ(t1), + + /* Set z to 1 (in Montgomery representation). */ + MMUL(P1z, P2x, P2z), + + ENDCODE +}; + +/* + * Conversion back to affine coordinates. This code snippet assumes that + * the z coordinate of P2 is set to 1 (not in Montgomery representation). + */ +static const uint16_t p384_code_affine[] = { + + /* Save z*R in t1. */ + MSET(t1, P1z), + + /* Compute z^3 in t2. */ + MMUL(t2, P1z, P1z), + MMUL(t3, P1z, t2), + MMUL(t2, t3, P2z), + + /* Invert to (1/z^3) in t2. */ + MINV(t2, t3, t4), + + /* Compute y. */ + MSET(t3, P1y), + MMUL(P1y, t2, t3), + + /* Compute (1/z^2) in t3. */ + MMUL(t3, t2, t1), + + /* Compute x. */ + MSET(t2, P1x), + MMUL(P1x, t2, t3), + + ENDCODE +}; + +static uint32_t +p384_run_code(struct p384_jacobian *P1, const struct p384_jacobian *P2, + const struct p384_curve_params *cc, const uint16_t *code) +{ + uint32_t r; + uint32_t t[13][I31_LEN]; + size_t u; + + r = 1; + + /* + * Copy the two operands in the dedicated registers. + */ + memcpy(t[P1x], P1->c, 3 * I31_LEN * sizeof(uint32_t)); + memcpy(t[P2x], P2->c, 3 * I31_LEN * sizeof(uint32_t)); + + /* + * Run formulas. + */ + for (u = 0;; u ++) { + unsigned op, d, a, b; + + op = code[u]; + if (op == 0) { + break; + } + d = (op >> 8) & 0x0F; + a = (op >> 4) & 0x0F; + b = op & 0x0F; + op >>= 12; + switch (op) { + uint32_t ctl; + size_t plen; + unsigned char tp[(MG_BR_MAX_EC_SIZE + 7) >> 3]; + + case 0: + memcpy(t[d], t[a], I31_LEN * sizeof(uint32_t)); + break; + case 1: + ctl = mg_br_i31_add(t[d], t[a], 1); + ctl |= MG_BR_NOT(mg_br_i31_sub(t[d], cc->p, 0)); + mg_br_i31_sub(t[d], cc->p, ctl); + break; + case 2: + mg_br_i31_add(t[d], cc->p, mg_br_i31_sub(t[d], t[a], 1)); + break; + case 3: + mg_br_i31_montymul(t[d], t[a], t[b], cc->p, cc->p0i); + break; + case 4: + plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3; + mg_br_i31_encode(tp, plen, cc->p); + tp[plen - 1] -= 2; + mg_br_i31_modpow(t[d], tp, plen, + cc->p, cc->p0i, t[a], t[b]); + break; + default: + r &= ~mg_br_i31_iszero(t[d]); + break; + } + } + + /* + * Copy back result. + */ + memcpy(P1->c, t[P1x], 3 * I31_LEN * sizeof(uint32_t)); + return r; +} + +static void +p384_set_one(uint32_t *x, const uint32_t *p) +{ + size_t plen; + + plen = (p[0] + 63) >> 5; + memset(x, 0, plen * sizeof *x); + x[0] = p[0]; + x[1] = 0x00000001; +} + +static void +p384_point_zero(struct p384_jacobian *P, const struct p384_curve_params *cc) +{ + memset(P, 0, sizeof *P); + P->c[0][0] = P->c[1][0] = P->c[2][0] = cc->p[0]; +} + +static inline void +p384_point_double(struct p384_jacobian *P, const struct p384_curve_params *cc) +{ + p384_run_code(P, P, cc, p384_code_double); +} + +static inline uint32_t +p384_point_add(struct p384_jacobian *P1, const struct p384_jacobian *P2, const struct p384_curve_params *cc) +{ + return p384_run_code(P1, P2, cc, p384_code_add); +} + +static void +p384_point_mul(struct p384_jacobian *P, const unsigned char *x, size_t xlen, + const struct p384_curve_params *cc) +{ + /* + * We do a simple double-and-add ladder with a 2-bit window + * to make only one add every two doublings. We thus first + * precompute 2P and 3P in some local buffers. + * + * We always perform two doublings and one addition; the + * addition is with P, 2P and 3P and is done in a temporary + * array. + * + * The addition code cannot handle cases where one of the + * operands is infinity, which is the case at the start of the + * ladder. We therefore need to maintain a flag that controls + * this situation. + */ + uint32_t qz; + struct p384_jacobian P2, P3, Q, T, U; + + memcpy(&P2, P, sizeof P2); + p384_point_double(&P2, cc); + memcpy(&P3, P, sizeof P3); + p384_point_add(&P3, &P2, cc); + + p384_point_zero(&Q, cc); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + p384_point_double(&Q, cc); + p384_point_double(&Q, cc); + memcpy(&T, P, sizeof T); + memcpy(&U, &Q, sizeof U); + bits = (*x >> k) & (uint32_t)3; + bnz = MG_BR_NEQ(bits, 0); + MG_BR_CCOPY(MG_BR_EQ(bits, 2), &T, &P2, sizeof T); + MG_BR_CCOPY(MG_BR_EQ(bits, 3), &T, &P3, sizeof T); + p384_point_add(&U, &T, cc); + MG_BR_CCOPY(bnz & qz, &Q, &T, sizeof Q); + MG_BR_CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + memcpy(P, &Q, sizeof Q); +} + +/* + * Decode point into Jacobian coordinates. This function does not support + * the point at infinity. If the point is invalid then this returns 0, but + * the coordinates are still set to properly formed field elements. + */ +static uint32_t +p384_point_decode(struct p384_jacobian *P, const void *src, size_t len, const struct p384_curve_params *cc) +{ + /* + * Points must use uncompressed format: + * -- first byte is 0x04; + * -- coordinates X and Y use unsigned big-endian, with the same + * length as the field modulus. + * + * We don't support hybrid format (uncompressed, but first byte + * has value 0x06 or 0x07, depending on the least significant bit + * of Y) because it is rather useless, and explicitly forbidden + * by PKIX (RFC 5480, section 2.2). + * + * We don't support compressed format either, because it is not + * much used in practice (there are or were patent-related + * concerns about point compression, which explains the lack of + * generalised support). Also, point compression support would + * need a bit more code. + */ + const unsigned char *buf; + size_t plen, zlen; + uint32_t r; + struct p384_jacobian Q; + + buf = (const unsigned char *)src; + p384_point_zero(P, cc); + plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3; + if (len != 1 + (plen << 1)) { + return 0; + } + r = mg_br_i31_decode_mod(P->c[0], buf + 1, plen, cc->p); + r &= mg_br_i31_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p); + + /* + * Check first byte. + */ + r &= MG_BR_EQ(buf[0], 0x04); + /* obsolete + r &= MG_BR_EQ(buf[0], 0x04) | (MG_BR_EQ(buf[0] & 0xFE, 0x06) + & ~(uint32_t)(buf[0] ^ buf[plen << 1])); + */ + + /* + * Convert coordinates and check that the point is valid. + */ + zlen = ((cc->p[0] + 63) >> 5) * sizeof(uint32_t); + memcpy(Q.c[0], cc->R2, zlen); + memcpy(Q.c[1], cc->b, zlen); + p384_set_one(Q.c[2], cc->p); + r &= ~p384_run_code(P, &Q, cc, p384_code_check); + return r; +} + +/* + * Encode a point. This method assumes that the point is correct and is + * not the point at infinity. Encoded size is always 1+2*plen, where + * plen is the field modulus length, in bytes. + */ +static void +p384_point_encode(void *dst, const struct p384_jacobian *P, const struct p384_curve_params *cc) +{ + unsigned char *buf; + uint32_t xbl; + size_t plen; + struct p384_jacobian Q, T; + + buf = (unsigned char *)dst; + xbl = cc->p[0]; + xbl -= (xbl >> 5); + plen = (xbl + 7) >> 3; + buf[0] = 0x04; + memcpy(&Q, P, sizeof *P); + p384_set_one(T.c[2], cc->p); + p384_run_code(&Q, &T, cc, p384_code_affine); + mg_br_i31_encode(buf + 1, plen, Q.c[0]); + mg_br_i31_encode(buf + 1 + plen, plen, Q.c[1]); +} + +static const unsigned char * +p384_api_generator(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P384_G; + return P384_G; +} + +static const unsigned char * +p384_api_order(int curve, size_t *len) +{ + (void)curve; + *len = sizeof P384_N; + return P384_N; +} + +static size_t +p384_api_xoff(int curve, size_t *len) +{ + p384_api_generator(curve, len); + *len >>= 1; + return 1; +} + +static uint32_t +p384_api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + const struct p384_curve_params *cc; + struct p384_jacobian P; + + cc = p384_id_to_curve(curve); + if (Glen != cc->point_len) { + return 0; + } + r = p384_point_decode(&P, G, Glen, cc); + p384_point_mul(&P, x, xlen, cc); + p384_point_encode(G, &P, cc); + return r; +} + +static size_t +p384_api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + const unsigned char *G; + size_t Glen; + + G = p384_api_generator(curve, &Glen); + memcpy(R, G, Glen); + p384_api_mul(R, Glen, x, xlen, curve); + return Glen; +} + +static uint32_t +p384_api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + uint32_t r, t, z; + const struct p384_curve_params *cc; + struct p384_jacobian P, Q; + + /* + * TODO: see about merging the two ladders. Right now, we do + * two independent point multiplications, which is a bit + * wasteful of CPU resources (but yields short code). + */ + + cc = p384_id_to_curve(curve); + if (len != cc->point_len) { + return 0; + } + r = p384_point_decode(&P, A, len, cc); + if (B == NULL) { + size_t Glen; + + B = p384_api_generator(curve, &Glen); + } + r &= p384_point_decode(&Q, B, len, cc); + p384_point_mul(&P, x, xlen, cc); + p384_point_mul(&Q, y, ylen, cc); + + /* + * We want to compute P+Q. Since the base points A and B are distinct + * from infinity, and the multipliers are non-zero and lower than the + * curve order, then we know that P and Q are non-infinity. This + * leaves two special situations to test for: + * -- If P = Q then we must use p384_point_double(). + * -- If P+Q = 0 then we must report an error. + */ + t = p384_point_add(&P, &Q, cc); + p384_point_double(&Q, cc); + z = mg_br_i31_iszero(P.c[2]); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + MG_BR_CCOPY(z & ~t, &P, &Q, sizeof Q); + p384_point_encode(A, &P, cc); + r &= ~(z & t); + + return r; +} + +static const struct mg_br_ec_impl mg_br_ec_prime_i31 = { + (uint32_t)1 << MG_BR_EC_secp384r1, + &p384_api_generator, + &p384_api_order, + &p384_api_xoff, + &p384_api_mul, + &p384_api_mulgen, + &p384_api_muladd +}; + +// See tls_p384.h +int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash, + size_t hash_size, const uint8_t signature[96]) { + unsigned char q[97]; + q[0] = 0x04; + memcpy(q + 1, public_key, 96); + return mg_br_ecdsa_i31_vrfy_raw(&mg_br_ec_prime_i31, MG_BR_EC_secp384r1, hash, + hash_size, q, sizeof(q), signature, 96) == 1; +} + +// Undefine local macros, mongoose.c is one translation unit +#undef I31_LEN +#undef MSET +#undef MADD +#undef MSUB +#undef MMUL +#undef MINV +#undef MTZ +#undef ENDCODE +#undef P1x +#undef P1y +#undef P1z +#undef P2x +#undef P2y +#undef P2z +#undef Px +#undef Py +#undef Pz +#undef t1 +#undef t2 +#undef t3 +#undef t4 +#undef t5 +#undef t6 +#undef t7 +#undef t8 +#undef t9 +#undef t10 +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) diff --git a/src/tls_p384.h b/src/tls_p384.h new file mode 100644 index 00000000..17881337 --- /dev/null +++ b/src/tls_p384.h @@ -0,0 +1,40 @@ +// P-384 from BearSSL, https://bearssl.org commit 7bea48e +// P-384 signature verification, used by built-in TLS for certificates and +// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c +// +// Copyright (c) 2016 Thomas Pornin +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static +// functions with p384_. Trimmed to P-384. Structs are not typedef-ed +#pragma once + +#include "arch.h" +#include "config.h" +#include "tls.h" + +#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) +// Verify ECDSA P-384 signature r || s over hash. Public key is X || Y. +// Return 1 on success, 0 on failure +int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash, + size_t hash_size, const uint8_t signature[96]); +#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) diff --git a/src/tls_uecc.c b/src/tls_uecc.c deleted file mode 100644 index cc50d18f..00000000 --- a/src/tls_uecc.c +++ /dev/null @@ -1,3304 +0,0 @@ -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#include "tls_uecc.h" -#include "tls.h" - -#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY) - -#ifndef MG_UECC_RNG_MAX_TRIES -#define MG_UECC_RNG_MAX_TRIES 64 -#endif - -#if MG_UECC_ENABLE_VLI_API -#define MG_UECC_VLI_API -#else -#define MG_UECC_VLI_API static -#endif - -#if (MG_UECC_PLATFORM == mg_uecc_avr) || (MG_UECC_PLATFORM == mg_uecc_arm) || \ - (MG_UECC_PLATFORM == mg_uecc_arm_thumb) || \ - (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) -#define MG_UECC_CONCATX(a, ...) a##__VA_ARGS__ -#define MG_UECC_CONCAT(a, ...) MG_UECC_CONCATX(a, __VA_ARGS__) - -#define STRX(a) #a -#define STR(a) STRX(a) - -#define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) -#define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) -#define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__)))) -#define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__)))) -#define EVAL4(...) __VA_ARGS__ - -#define DEC_1 0 -#define DEC_2 1 -#define DEC_3 2 -#define DEC_4 3 -#define DEC_5 4 -#define DEC_6 5 -#define DEC_7 6 -#define DEC_8 7 -#define DEC_9 8 -#define DEC_10 9 -#define DEC_11 10 -#define DEC_12 11 -#define DEC_13 12 -#define DEC_14 13 -#define DEC_15 14 -#define DEC_16 15 -#define DEC_17 16 -#define DEC_18 17 -#define DEC_19 18 -#define DEC_20 19 -#define DEC_21 20 -#define DEC_22 21 -#define DEC_23 22 -#define DEC_24 23 -#define DEC_25 24 -#define DEC_26 25 -#define DEC_27 26 -#define DEC_28 27 -#define DEC_29 28 -#define DEC_30 29 -#define DEC_31 30 -#define DEC_32 31 - -#define DEC_(N) MG_UECC_CONCAT(DEC_, N) - -#define SECOND_ARG(_, val, ...) val -#define SOME_CHECK_0 ~, 0 -#define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME, ) -#define SOME_OR_0(N) GET_SECOND_ARG(MG_UECC_CONCAT(SOME_CHECK_, N)) - -#define MG_UECC_EMPTY(...) -#define DEFER(...) __VA_ARGS__ MG_UECC_EMPTY() - -#define REPEAT_NAME_0() REPEAT_0 -#define REPEAT_NAME_SOME() REPEAT_SOME -#define REPEAT_0(...) -#define REPEAT_SOME(N, stuff) \ - DEFER(MG_UECC_CONCAT(REPEAT_NAME_, SOME_OR_0(DEC_(N))))()(DEC_(N), stuff) stuff -#define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff)) - -#define REPEATM_NAME_0() REPEATM_0 -#define REPEATM_NAME_SOME() REPEATM_SOME -#define REPEATM_0(...) -#define REPEATM_SOME(N, macro) \ - macro(N) DEFER(MG_UECC_CONCAT(REPEATM_NAME_, SOME_OR_0(DEC_(N))))()(DEC_(N), macro) -#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) -#endif - -// #include "platform-specific.inc" - -#if (MG_UECC_WORD_SIZE == 1) -#if MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_MAX_WORDS 21 /* Due to the size of curve_n. */ -#endif -#if MG_UECC_SUPPORTS_secp192r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 24 -#endif -#if MG_UECC_SUPPORTS_secp224r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 28 -#endif -#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1) -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 32 -#endif -#if MG_UECC_SUPPORTS_secp384r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 48 -#endif -#elif (MG_UECC_WORD_SIZE == 4) -#if MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_MAX_WORDS 6 /* Due to the size of curve_n. */ -#endif -#if MG_UECC_SUPPORTS_secp192r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 6 -#endif -#if MG_UECC_SUPPORTS_secp224r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 7 -#endif -#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1) -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 8 -#endif -#if MG_UECC_SUPPORTS_secp384r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 12 -#endif -#elif (MG_UECC_WORD_SIZE == 8) -#if MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_MAX_WORDS 3 -#endif -#if MG_UECC_SUPPORTS_secp192r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 3 -#endif -#if MG_UECC_SUPPORTS_secp224r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 4 -#endif -#if (MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1) -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 4 -#endif -#if MG_UECC_SUPPORTS_secp384r1 -#undef MG_UECC_MAX_WORDS -#define MG_UECC_MAX_WORDS 6 -#endif -#endif /* MG_UECC_WORD_SIZE */ - -#define BITS_TO_WORDS(num_bits) \ - ((wordcount_t) ((num_bits + ((MG_UECC_WORD_SIZE * 8) - 1)) / \ - (MG_UECC_WORD_SIZE * 8))) -#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) - -struct MG_UECC_Curve_t { - wordcount_t num_words; - wordcount_t num_bytes; - bitcount_t num_n_bits; - mg_uecc_word_t p[MG_UECC_MAX_WORDS]; - mg_uecc_word_t n[MG_UECC_MAX_WORDS]; - mg_uecc_word_t G[MG_UECC_MAX_WORDS * 2]; - mg_uecc_word_t b[MG_UECC_MAX_WORDS]; - void (*double_jacobian)(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve); -#if MG_UECC_SUPPORT_COMPRESSED_POINT - void (*mod_sqrt)(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif - void (*x_side)(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - void (*mmod_fast)(mg_uecc_word_t *result, mg_uecc_word_t *product); -#endif -}; - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN -static void bcopy(uint8_t *dst, const uint8_t *src, unsigned num_bytes) { - while (0 != num_bytes) { - num_bytes--; - dst[num_bytes] = src[num_bytes]; - } -} -#endif - -static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -#if (MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) -#include "asm_arm.inc" -#endif - -#if (MG_UECC_PLATFORM == mg_uecc_avr) -#include "asm_avr.inc" -#endif - -#ifndef asm_clear -#define asm_clear 0 -#endif -#ifndef asm_set -#define asm_set 0 -#endif -#ifndef asm_add -#define asm_add 0 -#endif -#ifndef asm_sub -#define asm_sub 0 -#endif -#ifndef asm_mult -#define asm_mult 0 -#endif -#ifndef asm_rshift1 -#define asm_rshift1 0 -#endif -#ifndef asm_mmod_fast_secp256r1 -#define asm_mmod_fast_secp256r1 0 -#endif - -static MG_UECC_RNG_Function g_rng_function = NULL; - -void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function) { - g_rng_function = rng_function; -} - -MG_UECC_RNG_Function mg_uecc_get_rng(void) { - return g_rng_function; -} - -int mg_uecc_curve_private_key_size(MG_UECC_Curve curve) { - return BITS_TO_BYTES(curve->num_n_bits); -} - -int mg_uecc_curve_public_key_size(MG_UECC_Curve curve) { - return 2 * curve->num_bytes; -} - -#if !asm_clear -MG_UECC_VLI_API void mg_uecc_vli_clear(mg_uecc_word_t *vli, - wordcount_t num_words) { - wordcount_t i; - for (i = 0; i < num_words; ++i) { - vli[i] = 0; - } -} -#endif /* !asm_clear */ - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, - wordcount_t num_words) { - mg_uecc_word_t bits = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - bits |= vli[i]; - } - return (bits == 0); -} - -/* Returns nonzero if bit 'bit' of vli is set. */ -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, - bitcount_t bit) { - return (vli[bit >> MG_UECC_WORD_BITS_SHIFT] & - ((mg_uecc_word_t) 1 << (bit & MG_UECC_WORD_BITS_MASK))); -} - -/* Counts the number of words in vli. */ -static wordcount_t vli_numDigits(const mg_uecc_word_t *vli, - const wordcount_t max_words) { - wordcount_t i; - /* Search from the end until we find a non-zero digit. - We do it in reverse because we expect that most digits will be nonzero. */ - for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { - } - - return (i + 1); -} - -/* Counts the number of bits required to represent vli. */ -MG_UECC_VLI_API bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, - const wordcount_t max_words) { - mg_uecc_word_t i; - mg_uecc_word_t digit; - - wordcount_t num_digits = vli_numDigits(vli, max_words); - if (num_digits == 0) { - return 0; - } - - digit = vli[num_digits - 1]; - for (i = 0; digit; ++i) { - digit >>= 1; - } - - return (((bitcount_t) ((num_digits - 1) << MG_UECC_WORD_BITS_SHIFT)) + - (bitcount_t) i); -} - -/* Sets dest = src. */ -#if !asm_set -MG_UECC_VLI_API void mg_uecc_vli_set(mg_uecc_word_t *dest, - const mg_uecc_word_t *src, - wordcount_t num_words) { - wordcount_t i; - for (i = 0; i < num_words; ++i) { - dest[i] = src[i]; - } -} -#endif /* !asm_set */ - -/* Returns sign of left - right. */ -static cmpresult_t mg_uecc_vli_cmp_unsafe(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - wordcount_t i; - for (i = num_words - 1; i >= 0; --i) { - if (left[i] > right[i]) { - return 1; - } else if (left[i] < right[i]) { - return -1; - } - } - return 0; -} - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise. */ -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t diff = 0; - wordcount_t i; - for (i = num_words - 1; i >= 0; --i) { - diff |= (left[i] ^ right[i]); - } - return (diff == 0); -} - -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Returns sign of left - right, in constant time. */ -MG_UECC_VLI_API cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t tmp[MG_UECC_MAX_WORDS]; - mg_uecc_word_t neg = !!mg_uecc_vli_sub(tmp, left, right, num_words); - mg_uecc_word_t equal = mg_uecc_vli_isZero(tmp, num_words); - return (cmpresult_t) (!equal - 2 * neg); -} - -/* Computes vli = vli >> 1. */ -#if !asm_rshift1 -MG_UECC_VLI_API void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, - wordcount_t num_words) { - mg_uecc_word_t *end = vli; - mg_uecc_word_t carry = 0; - - vli += num_words; - while (vli-- > end) { - mg_uecc_word_t temp = *vli; - *vli = (temp >> 1) | carry; - carry = temp << (MG_UECC_WORD_BITS - 1); - } -} -#endif /* !asm_rshift1 */ - -/* Computes result = left + right, returning carry. Can modify in place. */ -#if !asm_add -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t carry = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - mg_uecc_word_t sum = left[i] + right[i] + carry; - if (sum != left[i]) { - carry = (sum < left[i]); - } - result[i] = sum; - } - return carry; -} -#endif /* !asm_add */ - -/* Computes result = left - right, returning borrow. Can modify in place. */ -#if !asm_sub -MG_UECC_VLI_API mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t borrow = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - mg_uecc_word_t diff = left[i] - right[i] - borrow; - if (diff != left[i]) { - borrow = (diff > left[i]); - } - result[i] = diff; - } - return borrow; -} -#endif /* !asm_sub */ - -#if !asm_mult || (MG_UECC_SQUARE_FUNC && !asm_square) || \ - (MG_UECC_SUPPORTS_secp256k1 && (MG_UECC_OPTIMIZATION_LEVEL > 0) && \ - ((MG_UECC_WORD_SIZE == 1) || (MG_UECC_WORD_SIZE == 8))) -static void muladd(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0, - mg_uecc_word_t *r1, mg_uecc_word_t *r2) { -#if MG_UECC_WORD_SIZE == 8 - uint64_t a0 = a & 0xffffffff; - uint64_t a1 = a >> 32; - uint64_t b0 = b & 0xffffffff; - uint64_t b1 = b >> 32; - - uint64_t i0 = a0 * b0; - uint64_t i1 = a0 * b1; - uint64_t i2 = a1 * b0; - uint64_t i3 = a1 * b1; - - uint64_t p0, p1; - - i2 += (i0 >> 32); - i2 += i1; - if (i2 < i1) { /* overflow */ - i3 += 0x100000000; - } - - p0 = (i0 & 0xffffffff) | (i2 << 32); - p1 = i3 + (i2 >> 32); - - *r0 += p0; - *r1 += (p1 + (*r0 < p0)); - *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); -#else - mg_uecc_dword_t p = (mg_uecc_dword_t) a * b; - mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0; - r01 += p; - *r2 += (r01 < p); - *r1 = (mg_uecc_word_t) (r01 >> MG_UECC_WORD_BITS); - *r0 = (mg_uecc_word_t) r01; -#endif -} -#endif /* muladd needed */ - -#if !asm_mult -MG_UECC_VLI_API void mg_uecc_vli_mult(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words) { - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - wordcount_t i, k; - - /* Compute each digit of result in sequence, maintaining the carries. */ - for (k = 0; k < num_words; ++k) { - for (i = 0; i <= k; ++i) { - muladd(left[i], right[k - i], &r0, &r1, &r2); - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - for (k = num_words; k < num_words * 2 - 1; ++k) { - for (i = (wordcount_t) ((k + 1) - num_words); i < num_words; ++i) { - muladd(left[i], right[k - i], &r0, &r1, &r2); - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - result[num_words * 2 - 1] = r0; -} -#endif /* !asm_mult */ - -#if MG_UECC_SQUARE_FUNC - -#if !asm_square -static void mul2add(mg_uecc_word_t a, mg_uecc_word_t b, mg_uecc_word_t *r0, - mg_uecc_word_t *r1, mg_uecc_word_t *r2) { -#if MG_UECC_WORD_SIZE == 8 - uint64_t a0 = a & 0xffffffffull; - uint64_t a1 = a >> 32; - uint64_t b0 = b & 0xffffffffull; - uint64_t b1 = b >> 32; - - uint64_t i0 = a0 * b0; - uint64_t i1 = a0 * b1; - uint64_t i2 = a1 * b0; - uint64_t i3 = a1 * b1; - - uint64_t p0, p1; - - i2 += (i0 >> 32); - i2 += i1; - if (i2 < i1) { /* overflow */ - i3 += 0x100000000ull; - } - - p0 = (i0 & 0xffffffffull) | (i2 << 32); - p1 = i3 + (i2 >> 32); - - *r2 += (p1 >> 63); - p1 = (p1 << 1) | (p0 >> 63); - p0 <<= 1; - - *r0 += p0; - *r1 += (p1 + (*r0 < p0)); - *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); -#else - mg_uecc_dword_t p = (mg_uecc_dword_t) a * b; - mg_uecc_dword_t r01 = ((mg_uecc_dword_t) (*r1) << MG_UECC_WORD_BITS) | *r0; - *r2 += (p >> (MG_UECC_WORD_BITS * 2 - 1)); - p *= 2; - r01 += p; - *r2 += (r01 < p); - *r1 = r01 >> MG_UECC_WORD_BITS; - *r0 = (mg_uecc_word_t) r01; -#endif -} - -MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - wordcount_t num_words) { - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - - wordcount_t i, k; - - for (k = 0; k < num_words * 2 - 1; ++k) { - mg_uecc_word_t min = (k < num_words ? 0 : (k + 1) - num_words); - for (i = min; i <= k && i <= k - i; ++i) { - if (i < k - i) { - mul2add(left[i], left[k - i], &r0, &r1, &r2); - } else { - muladd(left[i], left[k - i], &r0, &r1, &r2); - } - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - - result[num_words * 2 - 1] = r0; -} -#endif /* !asm_square */ - -#else /* MG_UECC_SQUARE_FUNC */ - -#if MG_UECC_ENABLE_VLI_API -MG_UECC_VLI_API void mg_uecc_vli_square(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - wordcount_t num_words) { - mg_uecc_vli_mult(result, left, left, num_words); -} -#endif /* MG_UECC_ENABLE_VLI_API */ - -#endif /* MG_UECC_SQUARE_FUNC */ - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modAdd(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t carry = mg_uecc_vli_add(result, left, right, num_words); - if (carry || mg_uecc_vli_cmp_unsafe(mod, result, num_words) != 1) { - /* result > mod (result = mod + remainder), so subtract mod to get - * remainder. */ - mg_uecc_vli_sub(result, result, mod, num_words); - } -} - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modSub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t l_borrow = mg_uecc_vli_sub(result, left, right, num_words); - if (l_borrow) { - /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, - we can get the correct result from result + mod (with overflow). */ - mg_uecc_vli_add(result, result, mod, num_words); - } -} - -/* Computes result = product % mod, where product is 2N words long. */ -/* Currently only designed to work for curve_p or curve_n. */ -MG_UECC_VLI_API void mg_uecc_vli_mmod(mg_uecc_word_t *result, - mg_uecc_word_t *product, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t mod_multiple[2 * MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp[2 * MG_UECC_MAX_WORDS]; - mg_uecc_word_t *v[2] = {tmp, product}; - mg_uecc_word_t index; - - /* Shift mod so its highest set bit is at the maximum position. */ - bitcount_t shift = (bitcount_t) ((num_words * 2 * MG_UECC_WORD_BITS) - - mg_uecc_vli_numBits(mod, num_words)); - wordcount_t word_shift = (wordcount_t) (shift / MG_UECC_WORD_BITS); - wordcount_t bit_shift = (wordcount_t) (shift % MG_UECC_WORD_BITS); - mg_uecc_word_t carry = 0; - mg_uecc_vli_clear(mod_multiple, word_shift); - if (bit_shift > 0) { - for (index = 0; index < (mg_uecc_word_t) num_words; ++index) { - mod_multiple[(mg_uecc_word_t) word_shift + index] = - (mg_uecc_word_t) (mod[index] << bit_shift) | carry; - carry = mod[index] >> (MG_UECC_WORD_BITS - bit_shift); - } - } else { - mg_uecc_vli_set(mod_multiple + word_shift, mod, num_words); - } - - for (index = 1; shift >= 0; --shift) { - mg_uecc_word_t borrow = 0; - wordcount_t i; - for (i = 0; i < num_words * 2; ++i) { - mg_uecc_word_t diff = v[index][i] - mod_multiple[i] - borrow; - if (diff != v[index][i]) { - borrow = (diff > v[index][i]); - } - v[1 - index][i] = diff; - } - index = !(index ^ borrow); /* Swap the index if there was no borrow */ - mg_uecc_vli_rshift1(mod_multiple, num_words); - mod_multiple[num_words - 1] |= mod_multiple[num_words] - << (MG_UECC_WORD_BITS - 1); - mg_uecc_vli_rshift1(mod_multiple + num_words, num_words); - } - mg_uecc_vli_set(result, v[index], num_words); -} - -/* Computes result = (left * right) % mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modMult(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_mult(product, left, right, num_words); - mg_uecc_vli_mmod(result, product, mod, num_words); -} - -MG_UECC_VLI_API void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - MG_UECC_Curve curve) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_mult(product, left, right, curve->num_words); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - mg_uecc_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -#if MG_UECC_SQUARE_FUNC - -#if MG_UECC_ENABLE_VLI_API -/* Computes result = left^2 % mod. */ -MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_square(product, left, num_words); - mg_uecc_vli_mmod(result, product, mod, num_words); -} -#endif /* MG_UECC_ENABLE_VLI_API */ - -MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve) { - mg_uecc_word_t product[2 * MG_UECC_MAX_WORDS]; - mg_uecc_vli_square(product, left, curve->num_words); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - mg_uecc_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -#else /* MG_UECC_SQUARE_FUNC */ - -#if MG_UECC_ENABLE_VLI_API -MG_UECC_VLI_API void mg_uecc_vli_modSquare(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_vli_modMult(result, left, left, mod, num_words); -} -#endif /* MG_UECC_ENABLE_VLI_API */ - -MG_UECC_VLI_API void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve) { - mg_uecc_vli_modMult_fast(result, left, left, curve); -} - -#endif /* MG_UECC_SQUARE_FUNC */ - -#define EVEN(vli) (!(vli[0] & 1)) -static void vli_modInv_update(mg_uecc_word_t *uv, const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t carry = 0; - if (!EVEN(uv)) { - carry = mg_uecc_vli_add(uv, uv, mod, num_words); - } - mg_uecc_vli_rshift1(uv, num_words); - if (carry) { - uv[num_words - 1] |= HIGH_BIT_SET; - } -} - -/* Computes result = (1 / input) % mod. All VLIs are the same size. - See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */ -MG_UECC_VLI_API void mg_uecc_vli_modInv(mg_uecc_word_t *result, - const mg_uecc_word_t *input, - const mg_uecc_word_t *mod, - wordcount_t num_words) { - mg_uecc_word_t a[MG_UECC_MAX_WORDS], b[MG_UECC_MAX_WORDS], - u[MG_UECC_MAX_WORDS], v[MG_UECC_MAX_WORDS]; - cmpresult_t cmpResult; - - if (mg_uecc_vli_isZero(input, num_words)) { - mg_uecc_vli_clear(result, num_words); - return; - } - - mg_uecc_vli_set(a, input, num_words); - mg_uecc_vli_set(b, mod, num_words); - mg_uecc_vli_clear(u, num_words); - u[0] = 1; - mg_uecc_vli_clear(v, num_words); - while ((cmpResult = mg_uecc_vli_cmp_unsafe(a, b, num_words)) != 0) { - if (EVEN(a)) { - mg_uecc_vli_rshift1(a, num_words); - vli_modInv_update(u, mod, num_words); - } else if (EVEN(b)) { - mg_uecc_vli_rshift1(b, num_words); - vli_modInv_update(v, mod, num_words); - } else if (cmpResult > 0) { - mg_uecc_vli_sub(a, a, b, num_words); - mg_uecc_vli_rshift1(a, num_words); - if (mg_uecc_vli_cmp_unsafe(u, v, num_words) < 0) { - mg_uecc_vli_add(u, u, mod, num_words); - } - mg_uecc_vli_sub(u, u, v, num_words); - vli_modInv_update(u, mod, num_words); - } else { - mg_uecc_vli_sub(b, b, a, num_words); - mg_uecc_vli_rshift1(b, num_words); - if (mg_uecc_vli_cmp_unsafe(v, u, num_words) < 0) { - mg_uecc_vli_add(v, v, mod, num_words); - } - mg_uecc_vli_sub(v, v, u, num_words); - vli_modInv_update(v, mod, num_words); - } - } - mg_uecc_vli_set(result, u, num_words); -} - -/* ------ Point operations ------ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_CURVE_SPECIFIC_H_ -#define _UECC_CURVE_SPECIFIC_H_ - -#define num_bytes_secp160r1 20 -#define num_bytes_secp192r1 24 -#define num_bytes_secp224r1 28 -#define num_bytes_secp256r1 32 -#define num_bytes_secp256k1 32 -#define num_bytes_secp384r1 48 - -#if (MG_UECC_WORD_SIZE == 1) - -#define num_words_secp160r1 20 -#define num_words_secp192r1 24 -#define num_words_secp224r1 28 -#define num_words_secp256r1 32 -#define num_words_secp256k1 32 -#define num_words_secp384r1 48 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \ - 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d - -#elif (MG_UECC_WORD_SIZE == 4) - -#define num_words_secp160r1 5 -#define num_words_secp192r1 6 -#define num_words_secp224r1 7 -#define num_words_secp256r1 8 -#define num_words_secp256k1 8 -#define num_words_secp384r1 12 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a - -#elif (MG_UECC_WORD_SIZE == 8) - -#define num_words_secp160r1 3 -#define num_words_secp192r1 3 -#define num_words_secp224r1 4 -#define num_words_secp256r1 4 -#define num_words_secp256k1 4 -#define num_words_secp384r1 6 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##U -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##U - -#endif /* MG_UECC_WORD_SIZE */ - -#if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \ - MG_UECC_SUPPORTS_secp224r1 || MG_UECC_SUPPORTS_secp256r1 || \ - MG_UECC_SUPPORTS_secp384r1 -static void double_jacobian_default(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve) { - /* t1 = X, t2 = Y, t3 = Z */ - mg_uecc_word_t t4[MG_UECC_MAX_WORDS]; - mg_uecc_word_t t5[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - if (mg_uecc_vli_isZero(Z1, num_words)) { - return; - } - - mg_uecc_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ - mg_uecc_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ - mg_uecc_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ - mg_uecc_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ - mg_uecc_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ - - mg_uecc_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ - mg_uecc_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ - mg_uecc_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ - mg_uecc_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ - - mg_uecc_vli_modAdd(Z1, X1, X1, curve->p, - num_words); /* t3 = 2*(x1^2 - z1^4) */ - mg_uecc_vli_modAdd(X1, X1, Z1, curve->p, - num_words); /* t1 = 3*(x1^2 - z1^4) */ - if (mg_uecc_vli_testBit(X1, 0)) { - mg_uecc_word_t l_carry = mg_uecc_vli_add(X1, X1, curve->p, num_words); - mg_uecc_vli_rshift1(X1, num_words); - X1[num_words - 1] |= l_carry << (MG_UECC_WORD_BITS - 1); - } else { - mg_uecc_vli_rshift1(X1, num_words); - } - /* t1 = 3/2*(x1^2 - z1^4) = B */ - - mg_uecc_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ - mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ - mg_uecc_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ - mg_uecc_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ - mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ - mg_uecc_vli_modSub(t4, X1, t4, curve->p, - num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ - - mg_uecc_vli_set(X1, Z1, num_words); - mg_uecc_vli_set(Z1, Y1, num_words); - mg_uecc_vli_set(Y1, t4, num_words); -} - -/* Computes result = x^3 + ax + b. result must not overlap x. */ -static void x_side_default(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve) { - mg_uecc_word_t _3[MG_UECC_MAX_WORDS] = {3}; /* -a = 3 */ - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */ - mg_uecc_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ - mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ - mg_uecc_vli_modAdd(result, result, curve->b, curve->p, - num_words); /* r = x^3 - 3x + b */ -} -#endif /* MG_UECC_SUPPORTS_secp... */ - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -#if MG_UECC_SUPPORTS_secp160r1 || MG_UECC_SUPPORTS_secp192r1 || \ - MG_UECC_SUPPORTS_secp256r1 || MG_UECC_SUPPORTS_secp256k1 || \ - MG_UECC_SUPPORTS_secp384r1 -/* Compute a = sqrt(a) (mod curve_p). */ -static void mod_sqrt_default(mg_uecc_word_t *a, MG_UECC_Curve curve) { - bitcount_t i; - mg_uecc_word_t p1[MG_UECC_MAX_WORDS] = {1}; - mg_uecc_word_t l_result[MG_UECC_MAX_WORDS] = {1}; - wordcount_t num_words = curve->num_words; - - /* When curve->p == 3 (mod 4), we can compute - sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */ - mg_uecc_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ - for (i = mg_uecc_vli_numBits(p1, num_words) - 1; i > 1; --i) { - mg_uecc_vli_modSquare_fast(l_result, l_result, curve); - if (mg_uecc_vli_testBit(p1, i)) { - mg_uecc_vli_modMult_fast(l_result, l_result, a, curve); - } - } - mg_uecc_vli_set(a, l_result, num_words); -} -#endif /* MG_UECC_SUPPORTS_secp... */ -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -#if MG_UECC_SUPPORTS_secp160r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp160r1 = { - num_words_secp160r1, - num_bytes_secp160r1, - 161, /* num_n_bits */ - {BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9), - BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00)}, - {BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68), - BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E), - BYTES_TO_WORDS_4(68, B5, 96, 4A), - - BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04), - BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31), - BYTES_TO_WORDS_4(55, 28, A6, 23)}, - {BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81), - BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54), - BYTES_TO_WORDS_4(FC, BE, 97, 1C)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp160r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp160r1(void) { - return &curve_secp160r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) -/* Computes result = product % curve_p - see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354 - - Note that this only works if log2(omega) < log2(p) / 2 */ -static void omega_mult_secp160r1(mg_uecc_word_t *result, - const mg_uecc_word_t *right); -#if MG_UECC_WORD_SIZE == 8 -static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_word_t tmp[2 * num_words_secp160r1]; - mg_uecc_word_t copy; - - mg_uecc_vli_clear(tmp, num_words_secp160r1); - mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); - - omega_mult_secp160r1(tmp, - product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */ - - product[num_words_secp160r1 - 1] &= 0xffffffff; - copy = tmp[num_words_secp160r1 - 1]; - tmp[num_words_secp160r1 - 1] &= 0xffffffff; - mg_uecc_vli_add(result, product, tmp, - num_words_secp160r1); /* (C, r) = r + q */ - mg_uecc_vli_clear(product, num_words_secp160r1); - tmp[num_words_secp160r1 - 1] = copy; - omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */ - mg_uecc_vli_add(result, result, product, - num_words_secp160r1); /* (C1, r) = r + Rq*c */ - - while (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p, - num_words_secp160r1) > 0) { - mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } -} - -static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) { - uint32_t carry; - unsigned i; - - /* Multiply by (2^31 + 1). */ - carry = 0; - for (i = 0; i < num_words_secp160r1; ++i) { - uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32); - result[i] = (tmp << 31) + tmp + carry; - carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp)); - } - result[i] = carry; -} -#else -static void vli_mmod_fast_secp160r1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_word_t tmp[2 * num_words_secp160r1]; - mg_uecc_word_t carry; - - mg_uecc_vli_clear(tmp, num_words_secp160r1); - mg_uecc_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); - - omega_mult_secp160r1(tmp, - product + num_words_secp160r1); /* (Rq, q) = q * c */ - - carry = mg_uecc_vli_add(result, product, tmp, - num_words_secp160r1); /* (C, r) = r + q */ - mg_uecc_vli_clear(product, num_words_secp160r1); - omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */ - carry += mg_uecc_vli_add(result, result, product, - num_words_secp160r1); /* (C1, r) = r + Rq*c */ - - while (carry > 0) { - --carry; - mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } - if (mg_uecc_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > - 0) { - mg_uecc_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } -} -#endif - -#if MG_UECC_WORD_SIZE == 1 -static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) { - uint8_t carry; - uint8_t i; - - /* Multiply by (2^31 + 1). */ - mg_uecc_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */ - mg_uecc_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */ - result[3] = right[0] << 7; /* get last bit from shift */ - - carry = mg_uecc_vli_add(result, result, right, - num_words_secp160r1); /* 2^31 + 1 */ - for (i = num_words_secp160r1; carry; ++i) { - uint16_t sum = (uint16_t) result[i] + carry; - result[i] = (uint8_t) sum; - carry = sum >> 8; - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) { - uint32_t carry; - unsigned i; - - /* Multiply by (2^31 + 1). */ - mg_uecc_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */ - mg_uecc_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */ - result[0] = right[0] << 31; /* get last bit from shift */ - - carry = mg_uecc_vli_add(result, result, right, - num_words_secp160r1); /* 2^31 + 1 */ - for (i = num_words_secp160r1; carry; ++i) { - uint64_t sum = (uint64_t) result[i] + carry; - result[i] = (uint32_t) sum; - carry = sum >> 32; - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */ - -#endif /* MG_UECC_SUPPORTS_secp160r1 */ - -#if MG_UECC_SUPPORTS_secp192r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp192r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp192r1 = { - num_words_secp192r1, - num_bytes_secp192r1, - 192, /* num_n_bits */ - {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14), - BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4), - BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C), - BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18), - - BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73), - BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63), - BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07)}, - {BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE), - BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F), - BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp192r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp192r1(void) { - return &curve_secp192r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -/* Computes result = product % curve_p. - See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf - */ -#if MG_UECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp192r1]; - uint8_t carry; - - mg_uecc_vli_set(result, product, num_words_secp192r1); - - mg_uecc_vli_set(tmp, &product[24], num_words_secp192r1); - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = product[24]; - tmp[9] = product[25]; - tmp[10] = product[26]; - tmp[11] = product[27]; - tmp[12] = product[28]; - tmp[13] = product[29]; - tmp[14] = product[30]; - tmp[15] = product[31]; - tmp[16] = product[32]; - tmp[17] = product[33]; - tmp[18] = product[34]; - tmp[19] = product[35]; - tmp[20] = product[36]; - tmp[21] = product[37]; - tmp[22] = product[38]; - tmp[23] = product[39]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[8] = product[40]; - tmp[1] = tmp[9] = product[41]; - tmp[2] = tmp[10] = product[42]; - tmp[3] = tmp[11] = product[43]; - tmp[4] = tmp[12] = product[44]; - tmp[5] = tmp[13] = product[45]; - tmp[6] = tmp[14] = product[46]; - tmp[7] = tmp[15] = product[47]; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] = - tmp[23] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result, - num_words_secp192r1) != 1) { - carry -= - mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp192r1]; - int carry; - - mg_uecc_vli_set(result, product, num_words_secp192r1); - - mg_uecc_vli_set(tmp, &product[6], num_words_secp192r1); - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = 0; - tmp[2] = product[6]; - tmp[3] = product[7]; - tmp[4] = product[8]; - tmp[5] = product[9]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[2] = product[10]; - tmp[1] = tmp[3] = product[11]; - tmp[4] = tmp[5] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result, - num_words_secp192r1) != 1) { - carry -= - mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#else -static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp192r1]; - int carry; - - mg_uecc_vli_set(result, product, num_words_secp192r1); - - mg_uecc_vli_set(tmp, &product[3], num_words_secp192r1); - carry = (int) mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = 0; - tmp[1] = product[3]; - tmp[2] = product[4]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = product[5]; - tmp[2] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp192r1.p, result, - num_words_secp192r1) != 1) { - carry -= - mg_uecc_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */ - -#endif /* MG_UECC_SUPPORTS_secp192r1 */ - -#if MG_UECC_SUPPORTS_secp224r1 - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp224r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp224r1 = { - num_words_secp224r1, - num_bytes_secp224r1, - 224, /* num_n_bits */ - {BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13), - BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34), - BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A), - BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B), - BYTES_TO_WORDS_4(BD, 0C, 0E, B7), - - BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44), - BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD), - BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5), - BYTES_TO_WORDS_4(88, 63, 37, BD)}, - {BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27), - BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50), - BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C), - BYTES_TO_WORDS_4(85, 0A, 05, B4)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_secp224r1, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp224r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp224r1(void) { - return &curve_secp224r1; -} - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rs(mg_uecc_word_t *d1, mg_uecc_word_t *e1, - mg_uecc_word_t *f1, const mg_uecc_word_t *d0, - const mg_uecc_word_t *e0, - const mg_uecc_word_t *f0) { - mg_uecc_word_t t[num_words_secp224r1]; - - mg_uecc_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */ - mg_uecc_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */ - mg_uecc_vli_modAdd(d1, t, f0, curve_secp224r1.p, - num_words_secp224r1); /* d1 <-- t + f0 */ - mg_uecc_vli_modAdd(e1, e1, e1, curve_secp224r1.p, - num_words_secp224r1); /* e1 <-- e1 + e1 */ - mg_uecc_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */ - mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p, - num_words_secp224r1); /* f1 <-- f1 + f1 */ - mg_uecc_vli_modAdd(f1, f1, f1, curve_secp224r1.p, - num_words_secp224r1); /* f1 <-- f1 + f1 */ -} - -/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rss(mg_uecc_word_t *d1, mg_uecc_word_t *e1, - mg_uecc_word_t *f1, const mg_uecc_word_t *d0, - const mg_uecc_word_t *e0, - const mg_uecc_word_t *f0, - const bitcount_t j) { - bitcount_t i; - - mg_uecc_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */ - mg_uecc_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */ - mg_uecc_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */ - for (i = 1; i <= j; i++) { - mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */ - } -} - -/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rm(mg_uecc_word_t *d2, mg_uecc_word_t *e2, - mg_uecc_word_t *f2, const mg_uecc_word_t *c, - const mg_uecc_word_t *d0, - const mg_uecc_word_t *e0, - const mg_uecc_word_t *d1, - const mg_uecc_word_t *e1) { - mg_uecc_word_t t1[num_words_secp224r1]; - mg_uecc_word_t t2[num_words_secp224r1]; - - mg_uecc_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */ - mg_uecc_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */ - /* t1 <-- p - t1 */ - mg_uecc_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p, - num_words_secp224r1); - mg_uecc_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */ - mg_uecc_vli_modAdd(t2, t2, t1, curve_secp224r1.p, - num_words_secp224r1); /* t2 <-- t2 + t1 */ - mg_uecc_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */ - mg_uecc_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */ - mg_uecc_vli_modAdd(e2, e2, t1, curve_secp224r1.p, - num_words_secp224r1); /* e2 <-- e2 + t1 */ - mg_uecc_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */ - mg_uecc_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */ - /* f2 <-- p - f2 */ - mg_uecc_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p, - num_words_secp224r1); - mg_uecc_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */ -} - -/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rp(mg_uecc_word_t *d1, mg_uecc_word_t *e1, - mg_uecc_word_t *f1, const mg_uecc_word_t *c, - const mg_uecc_word_t *r) { - wordcount_t i; - wordcount_t pow2i = 1; - mg_uecc_word_t d0[num_words_secp224r1]; - mg_uecc_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */ - mg_uecc_word_t f0[num_words_secp224r1]; - - mg_uecc_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */ - /* f0 <-- p - c */ - mg_uecc_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p, - num_words_secp224r1); - for (i = 0; i <= 6; i++) { - mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0, - pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */ - mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0, - e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */ - mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ - mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ - mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ - pow2i *= 2; - } -} - -/* Compute a = sqrt(a) (mod curve_p). */ -/* Routine 3.2.8 mp_mod_sqrt_224; from - * http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1(mg_uecc_word_t *a, MG_UECC_Curve curve) { - (void) curve; - bitcount_t i; - mg_uecc_word_t e1[num_words_secp224r1]; - mg_uecc_word_t f1[num_words_secp224r1]; - mg_uecc_word_t d0[num_words_secp224r1]; - mg_uecc_word_t e0[num_words_secp224r1]; - mg_uecc_word_t f0[num_words_secp224r1]; - mg_uecc_word_t d1[num_words_secp224r1]; - - /* s = a; using constant instead of random value */ - mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */ - mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, - f0); /* RS (d1, e1, f1, d0, e0, f0) */ - for (i = 1; i <= 95; i++) { - mg_uecc_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ - mg_uecc_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ - mg_uecc_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ - mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, - f0); /* RS (d1, e1, f1, d0, e0, f0) */ - if (mg_uecc_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */ - break; - } - } - mg_uecc_vli_modInv(f1, e0, curve_secp224r1.p, - num_words_secp224r1); /* f1 <-- 1 / e0 */ - mg_uecc_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */ -} -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -#if MG_UECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp224r1]; - int8_t carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp224r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; - tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[28]; - tmp[13] = product[29]; - tmp[14] = product[30]; - tmp[15] = product[31]; - tmp[16] = product[32]; - tmp[17] = product[33]; - tmp[18] = product[34]; - tmp[19] = product[35]; - tmp[20] = product[36]; - tmp[21] = product[37]; - tmp[22] = product[38]; - tmp[23] = product[39]; - tmp[24] = product[40]; - tmp[25] = product[41]; - tmp[26] = product[42]; - tmp[27] = product[43]; - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[12] = product[44]; - tmp[13] = product[45]; - tmp[14] = product[46]; - tmp[15] = product[47]; - tmp[16] = product[48]; - tmp[17] = product[49]; - tmp[18] = product[50]; - tmp[19] = product[51]; - tmp[20] = product[52]; - tmp[21] = product[53]; - tmp[22] = product[54]; - tmp[23] = product[55]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = product[28]; - tmp[1] = product[29]; - tmp[2] = product[30]; - tmp[3] = product[31]; - tmp[4] = product[32]; - tmp[5] = product[33]; - tmp[6] = product[34]; - tmp[7] = product[35]; - tmp[8] = product[36]; - tmp[9] = product[37]; - tmp[10] = product[38]; - tmp[11] = product[39]; - tmp[12] = product[40]; - tmp[13] = product[41]; - tmp[14] = product[42]; - tmp[15] = product[43]; - tmp[16] = product[44]; - tmp[17] = product[45]; - tmp[18] = product[46]; - tmp[19] = product[47]; - tmp[20] = product[48]; - tmp[21] = product[49]; - tmp[22] = product[50]; - tmp[23] = product[51]; - tmp[24] = product[52]; - tmp[25] = product[53]; - tmp[26] = product[54]; - tmp[27] = product[55]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = product[44]; - tmp[1] = product[45]; - tmp[2] = product[46]; - tmp[3] = product[47]; - tmp[4] = product[48]; - tmp[5] = product[49]; - tmp[6] = product[50]; - tmp[7] = product[51]; - tmp[8] = product[52]; - tmp[9] = product[53]; - tmp[10] = product[54]; - tmp[11] = product[55]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp224r1.p, - num_words_secp224r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result, - num_words_secp224r1) != 1) { - carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p, - num_words_secp224r1); - } - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp224r1]; - int carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp224r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = 0; - tmp[3] = product[7]; - tmp[4] = product[8]; - tmp[5] = product[9]; - tmp[6] = product[10]; - carry = mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[3] = product[11]; - tmp[4] = product[12]; - tmp[5] = product[13]; - tmp[6] = 0; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = product[7]; - tmp[1] = product[8]; - tmp[2] = product[9]; - tmp[3] = product[10]; - tmp[4] = product[11]; - tmp[5] = product[12]; - tmp[6] = product[13]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = product[11]; - tmp[1] = product[12]; - tmp[2] = product[13]; - tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp224r1.p, - num_words_secp224r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result, - num_words_secp224r1) != 1) { - carry -= mg_uecc_vli_sub(result, result, curve_secp224r1.p, - num_words_secp224r1); - } - } -} -#else -static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp224r1]; - int carry = 0; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp224r1); - result[num_words_secp224r1 - 1] &= 0xffffffff; - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[3] & 0xffffffff00000000ull; - tmp[2] = product[4]; - tmp[3] = product[5] & 0xffffffff; - mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[1] = product[5] & 0xffffffff00000000ull; - tmp[2] = product[6]; - tmp[3] = 0; - mg_uecc_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = (product[3] >> 32) | (product[4] << 32); - tmp[1] = (product[4] >> 32) | (product[5] << 32); - tmp[2] = (product[5] >> 32) | (product[6] << 32); - tmp[3] = product[6] >> 32; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = product[6] >> 32; - tmp[2] = tmp[3] = 0; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp224r1.p, - num_words_secp224r1); - } while (carry < 0); - } else { - while (mg_uecc_vli_cmp_unsafe(curve_secp224r1.p, result, - num_words_secp224r1) != 1) { - mg_uecc_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); - } - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0) */ - -#endif /* MG_UECC_SUPPORTS_secp224r1 */ - -#if MG_UECC_SUPPORTS_secp256r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp256r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp256r1 = { - num_words_secp256r1, - num_bytes_secp256r1, - 256, /* num_n_bits */ - {BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), - BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), - BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), - BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), - BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), - - BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), - BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), - BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), - BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F)}, - {BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), - BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), - BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), - BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp256r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp256r1(void) { - return &curve_secp256r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -#if MG_UECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp256r1]; - int8_t carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; - tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[44]; - tmp[13] = product[45]; - tmp[14] = product[46]; - tmp[15] = product[47]; - tmp[16] = product[48]; - tmp[17] = product[49]; - tmp[18] = product[50]; - tmp[19] = product[51]; - tmp[20] = product[52]; - tmp[21] = product[53]; - tmp[22] = product[54]; - tmp[23] = product[55]; - tmp[24] = product[56]; - tmp[25] = product[57]; - tmp[26] = product[58]; - tmp[27] = product[59]; - tmp[28] = product[60]; - tmp[29] = product[61]; - tmp[30] = product[62]; - tmp[31] = product[63]; - carry = mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[12] = product[48]; - tmp[13] = product[49]; - tmp[14] = product[50]; - tmp[15] = product[51]; - tmp[16] = product[52]; - tmp[17] = product[53]; - tmp[18] = product[54]; - tmp[19] = product[55]; - tmp[20] = product[56]; - tmp[21] = product[57]; - tmp[22] = product[58]; - tmp[23] = product[59]; - tmp[24] = product[60]; - tmp[25] = product[61]; - tmp[26] = product[62]; - tmp[27] = product[63]; - tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0; - carry += mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[32]; - tmp[1] = product[33]; - tmp[2] = product[34]; - tmp[3] = product[35]; - tmp[4] = product[36]; - tmp[5] = product[37]; - tmp[6] = product[38]; - tmp[7] = product[39]; - tmp[8] = product[40]; - tmp[9] = product[41]; - tmp[10] = product[42]; - tmp[11] = product[43]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[56]; - tmp[25] = product[57]; - tmp[26] = product[58]; - tmp[27] = product[59]; - tmp[28] = product[60]; - tmp[29] = product[61]; - tmp[30] = product[62]; - tmp[31] = product[63]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = product[36]; - tmp[1] = product[37]; - tmp[2] = product[38]; - tmp[3] = product[39]; - tmp[4] = product[40]; - tmp[5] = product[41]; - tmp[6] = product[42]; - tmp[7] = product[43]; - tmp[8] = product[44]; - tmp[9] = product[45]; - tmp[10] = product[46]; - tmp[11] = product[47]; - tmp[12] = product[52]; - tmp[13] = product[53]; - tmp[14] = product[54]; - tmp[15] = product[55]; - tmp[16] = product[56]; - tmp[17] = product[57]; - tmp[18] = product[58]; - tmp[19] = product[59]; - tmp[20] = product[60]; - tmp[21] = product[61]; - tmp[22] = product[62]; - tmp[23] = product[63]; - tmp[24] = product[52]; - tmp[25] = product[53]; - tmp[26] = product[54]; - tmp[27] = product[55]; - tmp[28] = product[32]; - tmp[29] = product[33]; - tmp[30] = product[34]; - tmp[31] = product[35]; - carry += mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = product[44]; - tmp[1] = product[45]; - tmp[2] = product[46]; - tmp[3] = product[47]; - tmp[4] = product[48]; - tmp[5] = product[49]; - tmp[6] = product[50]; - tmp[7] = product[51]; - tmp[8] = product[52]; - tmp[9] = product[53]; - tmp[10] = product[54]; - tmp[11] = product[55]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[32]; - tmp[25] = product[33]; - tmp[26] = product[34]; - tmp[27] = product[35]; - tmp[28] = product[40]; - tmp[29] = product[41]; - tmp[30] = product[42]; - tmp[31] = product[43]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[48]; - tmp[1] = product[49]; - tmp[2] = product[50]; - tmp[3] = product[51]; - tmp[4] = product[52]; - tmp[5] = product[53]; - tmp[6] = product[54]; - tmp[7] = product[55]; - tmp[8] = product[56]; - tmp[9] = product[57]; - tmp[10] = product[58]; - tmp[11] = product[59]; - tmp[12] = product[60]; - tmp[13] = product[61]; - tmp[14] = product[62]; - tmp[15] = product[63]; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[36]; - tmp[25] = product[37]; - tmp[26] = product[38]; - tmp[27] = product[39]; - tmp[28] = product[44]; - tmp[29] = product[45]; - tmp[30] = product[46]; - tmp[31] = product[47]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = product[52]; - tmp[1] = product[53]; - tmp[2] = product[54]; - tmp[3] = product[55]; - tmp[4] = product[56]; - tmp[5] = product[57]; - tmp[6] = product[58]; - tmp[7] = product[59]; - tmp[8] = product[60]; - tmp[9] = product[61]; - tmp[10] = product[62]; - tmp[11] = product[63]; - tmp[12] = product[32]; - tmp[13] = product[33]; - tmp[14] = product[34]; - tmp[15] = product[35]; - tmp[16] = product[36]; - tmp[17] = product[37]; - tmp[18] = product[38]; - tmp[19] = product[39]; - tmp[20] = product[40]; - tmp[21] = product[41]; - tmp[22] = product[42]; - tmp[23] = product[43]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - tmp[28] = product[48]; - tmp[29] = product[49]; - tmp[30] = product[50]; - tmp[31] = product[51]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[56]; - tmp[1] = product[57]; - tmp[2] = product[58]; - tmp[3] = product[59]; - tmp[4] = product[60]; - tmp[5] = product[61]; - tmp[6] = product[62]; - tmp[7] = product[63]; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[36]; - tmp[13] = product[37]; - tmp[14] = product[38]; - tmp[15] = product[39]; - tmp[16] = product[40]; - tmp[17] = product[41]; - tmp[18] = product[42]; - tmp[19] = product[43]; - tmp[20] = product[44]; - tmp[21] = product[45]; - tmp[22] = product[46]; - tmp[23] = product[47]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - tmp[28] = product[52]; - tmp[29] = product[53]; - tmp[30] = product[54]; - tmp[31] = product[55]; - carry -= mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += mg_uecc_vli_add(result, result, curve_secp256r1.p, - num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result, - num_words_secp256r1) != 1) { - carry -= mg_uecc_vli_sub(result, result, curve_secp256r1.p, - num_words_secp256r1); - } - } -} -#elif MG_UECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp256r1]; - int carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = 0; - tmp[3] = product[11]; - tmp[4] = product[12]; - tmp[5] = product[13]; - tmp[6] = product[14]; - tmp[7] = product[15]; - carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[3] = product[12]; - tmp[4] = product[13]; - tmp[5] = product[14]; - tmp[6] = product[15]; - tmp[7] = 0; - carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[8]; - tmp[1] = product[9]; - tmp[2] = product[10]; - tmp[3] = tmp[4] = tmp[5] = 0; - tmp[6] = product[14]; - tmp[7] = product[15]; - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = product[9]; - tmp[1] = product[10]; - tmp[2] = product[11]; - tmp[3] = product[13]; - tmp[4] = product[14]; - tmp[5] = product[15]; - tmp[6] = product[13]; - tmp[7] = product[8]; - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = product[11]; - tmp[1] = product[12]; - tmp[2] = product[13]; - tmp[3] = tmp[4] = tmp[5] = 0; - tmp[6] = product[8]; - tmp[7] = product[10]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[12]; - tmp[1] = product[13]; - tmp[2] = product[14]; - tmp[3] = product[15]; - tmp[4] = tmp[5] = 0; - tmp[6] = product[9]; - tmp[7] = product[11]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = product[13]; - tmp[1] = product[14]; - tmp[2] = product[15]; - tmp[3] = product[8]; - tmp[4] = product[9]; - tmp[5] = product[10]; - tmp[6] = 0; - tmp[7] = product[12]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[14]; - tmp[1] = product[15]; - tmp[2] = 0; - tmp[3] = product[9]; - tmp[4] = product[10]; - tmp[5] = product[11]; - tmp[6] = 0; - tmp[7] = product[13]; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p, - num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result, - num_words_secp256r1) != 1) { - carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p, - num_words_secp256r1); - } - } -} -#else -static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp256r1]; - int carry; - - /* t */ - mg_uecc_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[5] & 0xffffffff00000000U; - tmp[2] = product[6]; - tmp[3] = product[7]; - carry = (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[1] = product[6] << 32; - tmp[2] = (product[6] >> 32) | (product[7] << 32); - tmp[3] = product[7] >> 32; - carry += (int) mg_uecc_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[4]; - tmp[1] = product[5] & 0xffffffff; - tmp[2] = 0; - tmp[3] = product[7]; - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = (product[4] >> 32) | (product[5] << 32); - tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000U); - tmp[2] = product[7]; - tmp[3] = (product[6] >> 32) | (product[4] << 32); - carry += (int) mg_uecc_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = (product[6] >> 32); - tmp[2] = 0; - tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[6]; - tmp[1] = product[7]; - tmp[2] = 0; - tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000); - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = (product[6] >> 32) | (product[7] << 32); - tmp[1] = (product[7] >> 32) | (product[4] << 32); - tmp[2] = (product[4] >> 32) | (product[5] << 32); - tmp[3] = (product[6] << 32); - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[7]; - tmp[1] = product[4] & 0xffffffff00000000U; - tmp[2] = product[5]; - tmp[3] = product[6] & 0xffffffff00000000U; - carry -= (int) mg_uecc_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += (int) mg_uecc_vli_add(result, result, curve_secp256r1.p, - num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || mg_uecc_vli_cmp_unsafe(curve_secp256r1.p, result, - num_words_secp256r1) != 1) { - carry -= (int) mg_uecc_vli_sub(result, result, curve_secp256r1.p, - num_words_secp256r1); - } - } -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */ - -#endif /* MG_UECC_SUPPORTS_secp256r1 */ - -#if MG_UECC_SUPPORTS_secp256k1 - -static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve); -static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve); -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp256k1 = { - num_words_secp256k1, - num_bytes_secp256k1, - 256, /* num_n_bits */ - {BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF), - BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - {BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59), - BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02), - BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55), - BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79), - - BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C), - BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD), - BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D), - BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48)}, - {BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00)}, - &double_jacobian_secp256k1, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_secp256k1, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp256k1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp256k1(void) { - return &curve_secp256k1; -} - -/* Double in place */ -static void double_jacobian_secp256k1(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *Z1, MG_UECC_Curve curve) { - /* t1 = X, t2 = Y, t3 = Z */ - mg_uecc_word_t t4[num_words_secp256k1]; - mg_uecc_word_t t5[num_words_secp256k1]; - - if (mg_uecc_vli_isZero(Z1, num_words_secp256k1)) { - return; - } - - mg_uecc_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ - mg_uecc_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ - mg_uecc_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ - mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ - mg_uecc_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ - - mg_uecc_vli_modAdd(Y1, X1, X1, curve->p, - num_words_secp256k1); /* t2 = 2*x1^2 */ - mg_uecc_vli_modAdd(Y1, Y1, X1, curve->p, - num_words_secp256k1); /* t2 = 3*x1^2 */ - if (mg_uecc_vli_testBit(Y1, 0)) { - mg_uecc_word_t carry = - mg_uecc_vli_add(Y1, Y1, curve->p, num_words_secp256k1); - mg_uecc_vli_rshift1(Y1, num_words_secp256k1); - Y1[num_words_secp256k1 - 1] |= carry << (MG_UECC_WORD_BITS - 1); - } else { - mg_uecc_vli_rshift1(Y1, num_words_secp256k1); - } - /* t2 = 3/2*(x1^2) = B */ - - mg_uecc_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ - mg_uecc_vli_modSub(X1, X1, t4, curve->p, - num_words_secp256k1); /* t1 = B^2 - A */ - mg_uecc_vli_modSub(X1, X1, t4, curve->p, - num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ - - mg_uecc_vli_modSub(t4, t4, X1, curve->p, - num_words_secp256k1); /* t4 = A - x3 */ - mg_uecc_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ - mg_uecc_vli_modSub(Y1, Y1, t5, curve->p, - num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ -} - -/* Computes result = x^3 + b. result must not overlap x. */ -static void x_side_secp256k1(mg_uecc_word_t *result, const mg_uecc_word_t *x, - MG_UECC_Curve curve) { - mg_uecc_vli_modSquare_fast(result, x, curve); /* r = x^2 */ - mg_uecc_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ - mg_uecc_vli_modAdd(result, result, curve->b, curve->p, - num_words_secp256k1); /* r = x^3 + b */ -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1) -static void omega_mult_secp256k1(mg_uecc_word_t *result, - const mg_uecc_word_t *right); -static void vli_mmod_fast_secp256k1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_word_t tmp[2 * num_words_secp256k1]; - mg_uecc_word_t carry; - - mg_uecc_vli_clear(tmp, num_words_secp256k1); - mg_uecc_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); - - omega_mult_secp256k1(tmp, - product + num_words_secp256k1); /* (Rq, q) = q * c */ - - carry = mg_uecc_vli_add(result, product, tmp, - num_words_secp256k1); /* (C, r) = r + q */ - mg_uecc_vli_clear(product, num_words_secp256k1); - omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ - carry += mg_uecc_vli_add(result, result, product, - num_words_secp256k1); /* (C1, r) = r + Rq*c */ - - while (carry > 0) { - --carry; - mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); - } - if (mg_uecc_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > - 0) { - mg_uecc_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); - } -} - -#if MG_UECC_WORD_SIZE == 1 -static void omega_mult_secp256k1(uint8_t *result, const uint8_t *right) { - /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - wordcount_t k; - - /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - muladd(0xD1, right[0], &r0, &r1, &r2); - result[0] = r0; - r0 = r1; - r1 = r2; - /* r2 is still 0 */ - - for (k = 1; k < num_words_secp256k1; ++k) { - muladd(0x03, right[k - 1], &r0, &r1, &r2); - muladd(0xD1, right[k], &r0, &r1, &r2); - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2); - result[num_words_secp256k1] = r0; - result[num_words_secp256k1 + 1] = r1; - /* add the 2^32 multiple */ - result[4 + num_words_secp256k1] = - mg_uecc_vli_add(result + 4, result + 4, right, num_words_secp256k1); -} -#elif MG_UECC_WORD_SIZE == 4 -static void omega_mult_secp256k1(uint32_t *result, const uint32_t *right) { - /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - uint32_t carry = 0; - wordcount_t k; - - for (k = 0; k < num_words_secp256k1; ++k) { - uint64_t p = (uint64_t) 0x3D1 * right[k] + carry; - result[k] = (uint32_t) p; - carry = p >> 32; - } - result[num_words_secp256k1] = carry; - /* add the 2^32 multiple */ - result[1 + num_words_secp256k1] = - mg_uecc_vli_add(result + 1, result + 1, right, num_words_secp256k1); -} -#else -static void omega_mult_secp256k1(uint64_t *result, const uint64_t *right) { - mg_uecc_word_t r0 = 0; - mg_uecc_word_t r1 = 0; - mg_uecc_word_t r2 = 0; - wordcount_t k; - - /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - for (k = 0; k < num_words_secp256k1; ++k) { - muladd(0x1000003D1ull, right[k], &r0, &r1, &r2); - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - result[num_words_secp256k1] = r0; -} -#endif /* MG_UECC_WORD_SIZE */ -#endif /* (MG_UECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */ - -#endif /* MG_UECC_SUPPORTS_secp256k1 */ - -#if MG_UECC_SUPPORTS_secp384r1 - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp384r1(mg_uecc_word_t *result, - mg_uecc_word_t *product); -#endif - -static const struct MG_UECC_Curve_t curve_secp384r1 = { - num_words_secp384r1, - num_bytes_secp384r1, - 384, /* num_n_bits */ - /* p */ - {BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - /* n */ - {BYTES_TO_WORDS_8(73, 29, C5, CC, 6A, 19, EC, EC), - BYTES_TO_WORDS_8(7A, A7, B0, 48, B2, 0D, 1A, 58), - BYTES_TO_WORDS_8(DF, 2D, 37, F4, 81, 4D, 63, C7), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF)}, - /* G = (Gx || Gy) little-endian */ - {BYTES_TO_WORDS_8(B7, 0A, 76, 72, 38, 5E, 54, 3A), - BYTES_TO_WORDS_8(6C, 29, 55, BF, 5D, F2, 02, 55), - BYTES_TO_WORDS_8(38, 2A, 54, 82, E0, 41, F7, 59), - BYTES_TO_WORDS_8(98, 9B, A7, 8B, 62, 3B, 1D, 6E), - BYTES_TO_WORDS_8(74, AD, 20, F3, 1E, C7, B1, 8E), - BYTES_TO_WORDS_8(37, 05, 8B, BE, 22, CA, 87, AA), - - BYTES_TO_WORDS_8(5F, 0E, EA, 90, 7C, 1D, 43, 7A), - BYTES_TO_WORDS_8(9D, 81, 7E, 1D, CE, B1, 60, 0A), - BYTES_TO_WORDS_8(C0, B8, F0, B5, 13, 31, DA, E9), - BYTES_TO_WORDS_8(7C, 14, 9A, 28, BD, 1D, F4, F8), - BYTES_TO_WORDS_8(29, DC, 92, 92, BF, 98, 9E, 5D), - BYTES_TO_WORDS_8(6F, 2C, 26, 96, 4A, DE, 17, 36)}, - /* b */ - {BYTES_TO_WORDS_8(EF, 2A, EC, D3, ED, C8, 85, 2A), - BYTES_TO_WORDS_8(9D, D1, 2E, 8A, 8D, 39, 56, C6), - BYTES_TO_WORDS_8(5A, 87, 13, 50, 8F, 08, 14, 03), - BYTES_TO_WORDS_8(12, 41, 81, FE, 6E, 9C, 1D, 18), - BYTES_TO_WORDS_8(19, 2D, F8, E3, 6B, 05, 8E, 98), - BYTES_TO_WORDS_8(E4, E7, 3E, E2, A7, 2F, 31, B3)}, - &double_jacobian_default, -#if MG_UECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp384r1 -#endif -}; - -MG_UECC_Curve mg_uecc_secp384r1(void) { - return &curve_secp384r1; -} - -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp384r1(mg_uecc_word_t *result, - mg_uecc_word_t *product) { - mg_uecc_vli_mmod(result, product, curve_secp384r1.p, num_words_secp384r1); -} -#endif - -#endif /* MG_UECC_SUPPORTS_secp384r1 */ - -#endif /* _UECC_CURVE_SPECIFIC_H_ */ - -/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */ -#define EccPoint_isZero(point, curve) \ - mg_uecc_vli_isZero((point), (wordcount_t) ((curve)->num_words * 2)) - -/* Point multiplication algorithm using Montgomery's ladder with co-Z -coordinates. From http://eprint.iacr.org/2011/338.pdf -*/ - -/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ -static void apply_z(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - const mg_uecc_word_t *const Z, MG_UECC_Curve curve) { - mg_uecc_word_t t1[MG_UECC_MAX_WORDS]; - - mg_uecc_vli_modSquare_fast(t1, Z, curve); /* z^2 */ - mg_uecc_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ - mg_uecc_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ - mg_uecc_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ -} - -/* P = (x1, y1) => 2P, (x2, y2) => P' */ -static void XYcZ_initial_double(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *X2, mg_uecc_word_t *Y2, - const mg_uecc_word_t *const initial_Z, - MG_UECC_Curve curve) { - mg_uecc_word_t z[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - if (initial_Z) { - mg_uecc_vli_set(z, initial_Z, num_words); - } else { - mg_uecc_vli_clear(z, num_words); - z[0] = 1; - } - - mg_uecc_vli_set(X2, X1, num_words); - mg_uecc_vli_set(Y2, Y1, num_words); - - apply_z(X1, Y1, z, curve); - curve->double_jacobian(X1, Y1, z, curve); - apply_z(X2, Y2, z, curve); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) - or P => P', Q => P + Q -*/ -static void XYcZ_add(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, mg_uecc_word_t *X2, - mg_uecc_word_t *Y2, MG_UECC_Curve curve) { - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0}; - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ - mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ - mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ - mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ - mg_uecc_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ - - mg_uecc_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ - mg_uecc_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ - mg_uecc_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ - mg_uecc_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ - mg_uecc_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ - mg_uecc_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ - - mg_uecc_vli_set(X2, t5, num_words); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) - or P => P - Q, Q => P + Q -*/ -static void XYcZ_addC(mg_uecc_word_t *X1, mg_uecc_word_t *Y1, - mg_uecc_word_t *X2, mg_uecc_word_t *Y2, - MG_UECC_Curve curve) { - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - mg_uecc_word_t t5[MG_UECC_MAX_WORDS] = {0}; - mg_uecc_word_t t6[MG_UECC_MAX_WORDS]; - mg_uecc_word_t t7[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ - mg_uecc_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ - mg_uecc_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ - mg_uecc_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ - mg_uecc_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ - - mg_uecc_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ - mg_uecc_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ - mg_uecc_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ - mg_uecc_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ - mg_uecc_vli_modSub(X2, X2, t6, curve->p, - num_words); /* t3 = D - (B + C) = x3 */ - - mg_uecc_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ - mg_uecc_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ - mg_uecc_vli_modSub(Y2, Y2, Y1, curve->p, - num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ - - mg_uecc_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ - mg_uecc_vli_modSub(t7, t7, t6, curve->p, - num_words); /* t7 = F - (B + C) = x3' */ - mg_uecc_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ - mg_uecc_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ - mg_uecc_vli_modSub(Y1, t6, Y1, curve->p, - num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ - - mg_uecc_vli_set(X1, t7, num_words); -} - -/* result may overlap point. */ -static void EccPoint_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, - const mg_uecc_word_t *initial_Z, bitcount_t num_bits, - MG_UECC_Curve curve) { - /* R0 and R1 */ - mg_uecc_word_t Rx[2][MG_UECC_MAX_WORDS]; - mg_uecc_word_t Ry[2][MG_UECC_MAX_WORDS]; - mg_uecc_word_t z[MG_UECC_MAX_WORDS]; - bitcount_t i; - mg_uecc_word_t nb; - wordcount_t num_words = curve->num_words; - - mg_uecc_vli_set(Rx[1], point, num_words); - mg_uecc_vli_set(Ry[1], point + num_words, num_words); - - XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); - - for (i = num_bits - 2; i > 0; --i) { - nb = !mg_uecc_vli_testBit(scalar, i); - XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); - XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); - } - - nb = !mg_uecc_vli_testBit(scalar, 0); - XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); - - /* Find final 1/Z value. */ - mg_uecc_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ - mg_uecc_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ - mg_uecc_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ - mg_uecc_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ - /* yP / (xP * Yb * (X1 - X0)) */ - mg_uecc_vli_modMult_fast(z, z, point + num_words, curve); - mg_uecc_vli_modMult_fast(z, z, Rx[1 - nb], - curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ - /* End 1/Z calculation */ - - XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); - apply_z(Rx[0], Ry[0], z, curve); - - mg_uecc_vli_set(result, Rx[0], num_words); - mg_uecc_vli_set(result + num_words, Ry[0], num_words); -} - -static mg_uecc_word_t regularize_k(const mg_uecc_word_t *const k, - mg_uecc_word_t *k0, mg_uecc_word_t *k1, - MG_UECC_Curve curve) { - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - mg_uecc_word_t carry = - mg_uecc_vli_add(k0, k, curve->n, num_n_words) || - (num_n_bits < ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8) && - mg_uecc_vli_testBit(k0, num_n_bits)); - mg_uecc_vli_add(k1, k0, curve->n, num_n_words); - return carry; -} - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -MG_UECC_VLI_API int mg_uecc_generate_random_int(mg_uecc_word_t *random, - const mg_uecc_word_t *top, - wordcount_t num_words) { - mg_uecc_word_t mask = (mg_uecc_word_t) -1; - mg_uecc_word_t tries; - bitcount_t num_bits = mg_uecc_vli_numBits(top, num_words); - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - unsigned int len = (unsigned int) (num_words * MG_UECC_WORD_SIZE); - if (!(g_rng_function == NULL ? mg_random((uint8_t *) random, len) - : g_rng_function((uint8_t *) random, len))) - return 0; - random[num_words - 1] &= - mask >> ((bitcount_t) (num_words * MG_UECC_WORD_SIZE * 8 - num_bits)); - if (!mg_uecc_vli_isZero(random, num_words) && - mg_uecc_vli_cmp(top, random, num_words) == 1) { - return 1; - } - } - return 0; -} - -static mg_uecc_word_t EccPoint_compute_public_key(mg_uecc_word_t *result, - mg_uecc_word_t *private_key, - MG_UECC_Curve curve) { - mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *p2[2] = {tmp1, tmp2}; - mg_uecc_word_t *initial_Z = 0; - mg_uecc_word_t carry; - - /* Regularize the bitcount for the private key so that attackers cannot use a - side channel attack to learn the number of leading zeros. */ - carry = regularize_k(private_key, tmp1, tmp2, curve); - - /* If an RNG function was specified, try to get a random initial Z value to - improve protection against side-channel attacks. */ - if (g_rng_function) { - if (!mg_uecc_generate_random_int(p2[carry], curve->p, curve->num_words)) { - return 0; - } - initial_Z = p2[carry]; - } - EccPoint_mult(result, curve->G, p2[!carry], initial_Z, - (bitcount_t) (curve->num_n_bits + 1), curve); - - if (EccPoint_isZero(result, curve)) { - return 0; - } - return 1; -} - -#if MG_UECC_WORD_SIZE == 1 - -MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const uint8_t *native) { - wordcount_t i; - for (i = 0; i < num_bytes; ++i) { - bytes[i] = native[(num_bytes - 1) - i]; - } -} - -MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(uint8_t *native, - const uint8_t *bytes, - int num_bytes) { - mg_uecc_vli_nativeToBytes(native, num_bytes, bytes); -} - -#else - -MG_UECC_VLI_API void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const mg_uecc_word_t *native) { - int i; - for (i = 0; i < num_bytes; ++i) { - unsigned b = (unsigned) (num_bytes - 1 - i); - bytes[i] = (uint8_t) (native[b / MG_UECC_WORD_SIZE] >> - (8 * (b % MG_UECC_WORD_SIZE))); - } -} - -MG_UECC_VLI_API void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, - const uint8_t *bytes, - int num_bytes) { - int i; - mg_uecc_vli_clear(native, - (wordcount_t) ((num_bytes + (MG_UECC_WORD_SIZE - 1)) / - MG_UECC_WORD_SIZE)); - for (i = 0; i < num_bytes; ++i) { - unsigned b = (unsigned) (num_bytes - 1 - i); - native[b / MG_UECC_WORD_SIZE] |= (mg_uecc_word_t) bytes[i] - << (8 * (b % MG_UECC_WORD_SIZE)); - } -} - -#endif /* MG_UECC_WORD_SIZE */ - -int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, - MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key; - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _private[MG_UECC_MAX_WORDS]; - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t tries; - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - if (!mg_uecc_generate_random_int(_private, curve->n, - BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (EccPoint_compute_public_key(_public, _private, curve)) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), - _private); - mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public); - mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, - _public + curve->num_words); -#endif - return 1; - } - } - return 0; -} - -int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, - uint8_t *secret, MG_UECC_Curve curve) { - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; - mg_uecc_word_t _private[MG_UECC_MAX_WORDS]; - - mg_uecc_word_t tmp[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *p2[2] = {_private, tmp}; - mg_uecc_word_t *initial_Z = 0; - mg_uecc_word_t carry; - wordcount_t num_words = curve->num_words; - wordcount_t num_bytes = curve->num_bytes; - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) _private, private_key, num_bytes); - bcopy((uint8_t *) _public, public_key, num_bytes * 2); -#else - mg_uecc_vli_bytesToNative(_private, private_key, - BITS_TO_BYTES(curve->num_n_bits)); - mg_uecc_vli_bytesToNative(_public, public_key, num_bytes); - mg_uecc_vli_bytesToNative(_public + num_words, public_key + num_bytes, - num_bytes); -#endif - - /* Regularize the bitcount for the private key so that attackers cannot use a - side channel attack to learn the number of leading zeros. */ - carry = regularize_k(_private, _private, tmp, curve); - - /* If an RNG function was specified, try to get a random initial Z value to - improve protection against side-channel attacks. */ - if (g_rng_function) { - if (!mg_uecc_generate_random_int(p2[carry], curve->p, num_words)) { - return 0; - } - initial_Z = p2[carry]; - } - - EccPoint_mult(_public, _public, p2[!carry], initial_Z, - (bitcount_t) (curve->num_n_bits + 1), curve); -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes); -#else - mg_uecc_vli_nativeToBytes(secret, num_bytes, _public); -#endif - return !EccPoint_isZero(_public, curve); -} - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, - MG_UECC_Curve curve) { - wordcount_t i; - for (i = 0; i < curve->num_bytes; ++i) { - compressed[i + 1] = public_key[i]; - } -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01); -#else - compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); -#endif -} - -void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, - MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *point = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t point[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t *y = point + curve->num_words; -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy(public_key, compressed + 1, curve->num_bytes); -#else - mg_uecc_vli_bytesToNative(point, compressed + 1, curve->num_bytes); -#endif - curve->x_side(y, point, curve); - curve->mod_sqrt(y, curve); - - if ((uint8_t) (y[0] & 0x01) != (compressed[0] & 0x01)) { - mg_uecc_vli_sub(y, curve->p, y, curve->num_words); - } - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, point); - mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); -#endif -} -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -MG_UECC_VLI_API int mg_uecc_valid_point(const mg_uecc_word_t *point, - MG_UECC_Curve curve) { - mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - /* The point at infinity is invalid. */ - if (EccPoint_isZero(point, curve)) { - return 0; - } - - /* x and y must be smaller than p. */ - if (mg_uecc_vli_cmp_unsafe(curve->p, point, num_words) != 1 || - mg_uecc_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { - return 0; - } - - mg_uecc_vli_modSquare_fast(tmp1, point + num_words, curve); - curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ - - /* Make sure that y^2 == x^3 + ax + b */ - return (int) (mg_uecc_vli_equal(tmp1, tmp2, num_words)); -} - -int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes); - mg_uecc_vli_bytesToNative(_public + curve->num_words, - public_key + curve->num_bytes, curve->num_bytes); -#endif - return mg_uecc_valid_point(_public, curve); -} - -int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, - MG_UECC_Curve curve) { -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_private = (mg_uecc_word_t *) private_key; - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _private[MG_UECC_MAX_WORDS]; - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_bytesToNative(_private, private_key, - BITS_TO_BYTES(curve->num_n_bits)); -#endif - - /* Make sure the private key is in the range [1, n-1]. */ - if (mg_uecc_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (mg_uecc_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != - 1) { - return 0; - } - - /* Compute public key. */ - if (!EccPoint_compute_public_key(_public, _private, curve)) { - return 0; - } - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(public_key, curve->num_bytes, _public); - mg_uecc_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, - _public + curve->num_words); -#endif - return 1; -} - -/* -------- ECDSA code -------- */ - -static void bits2int(mg_uecc_word_t *native, const uint8_t *bits, - unsigned bits_size, MG_UECC_Curve curve) { - unsigned num_n_bytes = (unsigned) BITS_TO_BYTES(curve->num_n_bits); - unsigned num_n_words = (unsigned) BITS_TO_WORDS(curve->num_n_bits); - int shift; - mg_uecc_word_t carry; - mg_uecc_word_t *ptr; - - if (bits_size > num_n_bytes) { - bits_size = num_n_bytes; - } - - mg_uecc_vli_clear(native, (wordcount_t) num_n_words); -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) native, bits, bits_size); -#else - mg_uecc_vli_bytesToNative(native, bits, (int) bits_size); -#endif - if (bits_size * 8 <= (unsigned) curve->num_n_bits) { - return; - } - shift = (int) bits_size * 8 - curve->num_n_bits; - carry = 0; - ptr = native + num_n_words; - while (ptr-- > native) { - mg_uecc_word_t temp = *ptr; - *ptr = (temp >> shift) | carry; - carry = temp << (MG_UECC_WORD_BITS - shift); - } - - /* Reduce mod curve_n */ - if (mg_uecc_vli_cmp_unsafe(curve->n, native, (wordcount_t) num_n_words) != - 1) { - mg_uecc_vli_sub(native, native, curve->n, (wordcount_t) num_n_words); - } -} - -static int mg_uecc_sign_with_k_internal(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, mg_uecc_word_t *k, - uint8_t *signature, - MG_UECC_Curve curve) { - mg_uecc_word_t tmp[MG_UECC_MAX_WORDS]; - mg_uecc_word_t s[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *k2[2] = {tmp, s}; - mg_uecc_word_t *initial_Z = 0; -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *p = (mg_uecc_word_t *) signature; -#else - mg_uecc_word_t p[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t carry; - wordcount_t num_words = curve->num_words; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - - /* Make sure 0 < k < curve_n */ - if (mg_uecc_vli_isZero(k, num_words) || - mg_uecc_vli_cmp(curve->n, k, num_n_words) != 1) { - return 0; - } - - carry = regularize_k(k, tmp, s, curve); - /* If an RNG function was specified, try to get a random initial Z value to - improve protection against side-channel attacks. */ - if (g_rng_function) { - if (!mg_uecc_generate_random_int(k2[carry], curve->p, num_words)) { - return 0; - } - initial_Z = k2[carry]; - } - EccPoint_mult(p, curve->G, k2[!carry], initial_Z, - (bitcount_t) (num_n_bits + 1), curve); - if (mg_uecc_vli_isZero(p, num_words)) { - return 0; - } - - /* If an RNG function was specified, get a random number - to prevent side channel analysis of k. */ - if (!g_rng_function) { - mg_uecc_vli_clear(tmp, num_n_words); - tmp[0] = 1; - } else if (!mg_uecc_generate_random_int(tmp, curve->n, num_n_words)) { - return 0; - } - - /* Prevent side channel analysis of mg_uecc_vli_modInv() to determine - bits of k / the private key by premultiplying by a random number */ - mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ - mg_uecc_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ - mg_uecc_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - mg_uecc_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ -#endif - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); -#else - mg_uecc_vli_bytesToNative(tmp, private_key, - BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ -#endif - - s[num_n_words - 1] = 0; - mg_uecc_vli_set(s, p, num_words); - mg_uecc_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ - - bits2int(tmp, message_hash, hash_size, curve); - mg_uecc_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ - mg_uecc_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ - if (mg_uecc_vli_numBits(s, num_n_words) > (bitcount_t) curve->num_bytes * 8) { - return 0; - } -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s, - curve->num_bytes); -#else - mg_uecc_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); -#endif - return 1; -} - -#if 0 -/* For testing - sign with an explicitly specified k value */ -int mg_uecc_sign_with_k(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *k, uint8_t *signature, - MG_UECC_Curve curve) { - mg_uecc_word_t k2[MG_UECC_MAX_WORDS]; - bits2int(k2, k, (unsigned) BITS_TO_BYTES(curve->num_n_bits), curve); - return mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k2, - signature, curve); -} -#endif - -int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve) { - mg_uecc_word_t k[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tries; - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - if (!mg_uecc_generate_random_int(k, curve->n, - BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, k, - signature, curve)) { - return 1; - } - } - return 0; -} - -/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always - the same size as the hash result size. */ -static void HMAC_init(const MG_UECC_HashContext *hash_context, - const uint8_t *K) { - uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x36; - for (; i < hash_context->block_size; ++i) pad[i] = 0x36; - - hash_context->init_hash(hash_context); - hash_context->update_hash(hash_context, pad, hash_context->block_size); -} - -static void HMAC_update(const MG_UECC_HashContext *hash_context, - const uint8_t *message, unsigned message_size) { - hash_context->update_hash(hash_context, message, message_size); -} - -static void HMAC_finish(const MG_UECC_HashContext *hash_context, - const uint8_t *K, uint8_t *result) { - uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) pad[i] = K[i] ^ 0x5c; - for (; i < hash_context->block_size; ++i) pad[i] = 0x5c; - - hash_context->finish_hash(hash_context, result); - - hash_context->init_hash(hash_context); - hash_context->update_hash(hash_context, pad, hash_context->block_size); - hash_context->update_hash(hash_context, result, hash_context->result_size); - hash_context->finish_hash(hash_context, result); -} - -/* V = HMAC_K(V) */ -static void update_V(const MG_UECC_HashContext *hash_context, uint8_t *K, - uint8_t *V) { - HMAC_init(hash_context, K); - HMAC_update(hash_context, V, hash_context->result_size); - HMAC_finish(hash_context, K, V); -} - -/* Deterministic signing, similar to RFC 6979. Differences are: - * We just use H(m) directly rather than bits2octets(H(m)) - (it is not reduced modulo curve_n). - * We generate a value for k (aka T) directly rather than converting - endianness. - - Layout of hash_context->tmp: | | (1 byte overlapped 0x00 or 0x01) / - */ -int mg_uecc_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, unsigned hash_size, - const MG_UECC_HashContext *hash_context, - uint8_t *signature, MG_UECC_Curve curve) { - uint8_t *K = hash_context->tmp; - uint8_t *V = K + hash_context->result_size; - wordcount_t num_bytes = curve->num_bytes; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - mg_uecc_word_t tries; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) { - V[i] = 0x01; - K[i] = 0; - } - - /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x00; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_update(hash_context, private_key, (unsigned int) num_bytes); - HMAC_update(hash_context, message_hash, hash_size); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - - /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x01; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_update(hash_context, private_key, (unsigned int) num_bytes); - HMAC_update(hash_context, message_hash, hash_size); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - - for (tries = 0; tries < MG_UECC_RNG_MAX_TRIES; ++tries) { - mg_uecc_word_t T[MG_UECC_MAX_WORDS]; - uint8_t *T_ptr = (uint8_t *) T; - wordcount_t T_bytes = 0; - for (;;) { - update_V(hash_context, K, V); - for (i = 0; i < hash_context->result_size; ++i) { - T_ptr[T_bytes++] = V[i]; - if (T_bytes >= num_n_words * MG_UECC_WORD_SIZE) { - goto filled; - } - } - } - filled: - if ((bitcount_t) num_n_words * MG_UECC_WORD_SIZE * 8 > num_n_bits) { - mg_uecc_word_t mask = (mg_uecc_word_t) -1; - T[num_n_words - 1] &= - mask >> - ((bitcount_t) (num_n_words * MG_UECC_WORD_SIZE * 8 - num_n_bits)); - } - - if (mg_uecc_sign_with_k_internal(private_key, message_hash, hash_size, T, - signature, curve)) { - return 1; - } - - /* K = HMAC_K(V || 0x00) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x00; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - } - return 0; -} - -static bitcount_t smax(bitcount_t a, bitcount_t b) { - return (a > b ? a : b); -} - -int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *signature, - MG_UECC_Curve curve) { - mg_uecc_word_t u1[MG_UECC_MAX_WORDS], u2[MG_UECC_MAX_WORDS]; - mg_uecc_word_t z[MG_UECC_MAX_WORDS]; - mg_uecc_word_t sum[MG_UECC_MAX_WORDS * 2]; - mg_uecc_word_t rx[MG_UECC_MAX_WORDS]; - mg_uecc_word_t ry[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tx[MG_UECC_MAX_WORDS]; - mg_uecc_word_t ty[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tz[MG_UECC_MAX_WORDS]; - const mg_uecc_word_t *points[4]; - const mg_uecc_word_t *point; - bitcount_t num_bits; - bitcount_t i; -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - mg_uecc_word_t *_public = (mg_uecc_word_t *) public_key; -#else - mg_uecc_word_t _public[MG_UECC_MAX_WORDS * 2]; -#endif - mg_uecc_word_t r[MG_UECC_MAX_WORDS], s[MG_UECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - - rx[num_n_words - 1] = 0; - r[num_n_words - 1] = 0; - s[num_n_words - 1] = 0; - -#if MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) r, signature, curve->num_bytes); - bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes); -#else - mg_uecc_vli_bytesToNative(_public, public_key, curve->num_bytes); - mg_uecc_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes, - curve->num_bytes); - mg_uecc_vli_bytesToNative(r, signature, curve->num_bytes); - mg_uecc_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); -#endif - - /* r, s must not be 0. */ - if (mg_uecc_vli_isZero(r, num_words) || mg_uecc_vli_isZero(s, num_words)) { - return 0; - } - - /* r, s must be < n. */ - if (mg_uecc_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || - mg_uecc_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { - return 0; - } - - /* Calculate u1 and u2. */ - mg_uecc_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ - u1[num_n_words - 1] = 0; - bits2int(u1, message_hash, hash_size, curve); - mg_uecc_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ - mg_uecc_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ - - /* Calculate sum = G + Q. */ - mg_uecc_vli_set(sum, _public, num_words); - mg_uecc_vli_set(sum + num_words, _public + num_words, num_words); - mg_uecc_vli_set(tx, curve->G, num_words); - mg_uecc_vli_set(ty, curve->G + num_words, num_words); - mg_uecc_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ - XYcZ_add(tx, ty, sum, sum + num_words, curve); - mg_uecc_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ - apply_z(sum, sum + num_words, z, curve); - - /* Use Shamir's trick to calculate u1*G + u2*Q */ - points[0] = 0; - points[1] = curve->G; - points[2] = _public; - points[3] = sum; - num_bits = smax(mg_uecc_vli_numBits(u1, num_n_words), - mg_uecc_vli_numBits(u2, num_n_words)); - point = - points[(!!mg_uecc_vli_testBit(u1, (bitcount_t) (num_bits - 1))) | - ((!!mg_uecc_vli_testBit(u2, (bitcount_t) (num_bits - 1))) << 1)]; - mg_uecc_vli_set(rx, point, num_words); - mg_uecc_vli_set(ry, point + num_words, num_words); - mg_uecc_vli_clear(z, num_words); - z[0] = 1; - - for (i = num_bits - 2; i >= 0; --i) { - mg_uecc_word_t index; - curve->double_jacobian(rx, ry, z, curve); - - index = (!!mg_uecc_vli_testBit(u1, i)) | - (mg_uecc_word_t) ((!!mg_uecc_vli_testBit(u2, i)) << 1); - point = points[index]; - if (point) { - mg_uecc_vli_set(tx, point, num_words); - mg_uecc_vli_set(ty, point + num_words, num_words); - apply_z(tx, ty, z, curve); - mg_uecc_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ - XYcZ_add(tx, ty, rx, ry, curve); - mg_uecc_vli_modMult_fast(z, z, tz, curve); - } - } - - mg_uecc_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ - apply_z(rx, ry, z, curve); - - /* v = x1 (mod n) */ - if (mg_uecc_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { - mg_uecc_vli_sub(rx, rx, curve->n, num_n_words); - } - - /* Accept only if v == r. */ - return (int) (mg_uecc_vli_equal(rx, r, num_words)); -} - -#if MG_UECC_ENABLE_VLI_API - -unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve) { - return curve->num_words; -} - -unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve) { - return curve->num_bytes; -} - -unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve) { - return curve->num_bytes * 8; -} - -unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve) { - return BITS_TO_WORDS(curve->num_n_bits); -} - -unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve) { - return BITS_TO_BYTES(curve->num_n_bits); -} - -unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve) { - return curve->num_n_bits; -} - -const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve) { - return curve->p; -} - -const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve) { - return curve->n; -} - -const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve) { - return curve->G; -} - -const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve) { - return curve->b; -} - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve) { - curve->mod_sqrt(a, curve); -} -#endif - -void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, - MG_UECC_Curve curve) { -#if (MG_UECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - mg_uecc_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, MG_UECC_Curve curve) { - mg_uecc_word_t tmp1[MG_UECC_MAX_WORDS]; - mg_uecc_word_t tmp2[MG_UECC_MAX_WORDS]; - mg_uecc_word_t *p2[2] = {tmp1, tmp2}; - mg_uecc_word_t carry = regularize_k(scalar, tmp1, tmp2, curve); - - EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve); -} - -#endif /* MG_UECC_ENABLE_VLI_API */ -#endif // MG_TLS_BUILTIN -// End of uecc BSD-2 diff --git a/src/tls_uecc.h b/src/tls_uecc.h deleted file mode 100644 index 9e751e8d..00000000 --- a/src/tls_uecc.h +++ /dev/null @@ -1,659 +0,0 @@ -#pragma once -#include "arch.h" - -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_H_ -#define _UECC_H_ - -/* Platform selection options. -If MG_UECC_PLATFORM is not defined, the code will try to guess it based on -compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */ -#define mg_uecc_arch_other 0 -#define mg_uecc_x86 1 -#define mg_uecc_x86_64 2 -#define mg_uecc_arm 3 -#define mg_uecc_arm_thumb 4 -#define mg_uecc_arm_thumb2 5 -#define mg_uecc_arm64 6 -#define mg_uecc_avr 7 - -/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform -(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will -be automatically set based on your platform. */ - -/* Optimization level; trade speed for code size. - Larger values produce code that is faster but larger. - Currently supported values are 0 - 4; 0 is unusably slow for most - applications. Optimization level 4 currently only has an effect ARM platforms - where more than one curve is enabled. */ -#ifndef MG_UECC_OPTIMIZATION_LEVEL -#define MG_UECC_OPTIMIZATION_LEVEL 2 -#endif - -/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a -specific function to be used for (scalar) squaring instead of the generic -multiplication function. This can make things faster somewhat faster, but -increases the code size. */ -#ifndef MG_UECC_SQUARE_FUNC -#define MG_UECC_SQUARE_FUNC 0 -#endif - -/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will -switch to native little-endian format for *all* arrays passed in and out of the -public API. This includes public and private keys, shared secrets, signatures -and message hashes. Using this switch reduces the amount of call stack memory -used by uECC, since less intermediate translations are required. Note that this -will *only* work on native little-endian processors and it will treat the -uint8_t arrays passed into the public API as word arrays, therefore requiring -the provided byte arrays to be word aligned on architectures that do not support -unaligned accesses. IMPORTANT: Keys and signatures generated with -MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures -generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same -endianness. */ -#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN -#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0 -#endif - -/* Curve support selection. Set to 0 to remove that curve. */ -#ifndef MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_SUPPORTS_secp160r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp192r1 -#define MG_UECC_SUPPORTS_secp192r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp224r1 -#define MG_UECC_SUPPORTS_secp224r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp256r1 -#define MG_UECC_SUPPORTS_secp256r1 1 -#endif -#ifndef MG_UECC_SUPPORTS_secp256k1 -#define MG_UECC_SUPPORTS_secp256k1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp384r1 -#define MG_UECC_SUPPORTS_secp384r1 1 -#endif - -/* Specifies whether compressed point format is supported. - Set to 0 to disable point compression/decompression functions. */ -#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT -#define MG_UECC_SUPPORT_COMPRESSED_POINT 1 -#endif - -struct MG_UECC_Curve_t; -typedef const struct MG_UECC_Curve_t *MG_UECC_Curve; - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_SUPPORTS_secp160r1 -MG_UECC_Curve mg_uecc_secp160r1(void); -#endif -#if MG_UECC_SUPPORTS_secp192r1 -MG_UECC_Curve mg_uecc_secp192r1(void); -#endif -#if MG_UECC_SUPPORTS_secp224r1 -MG_UECC_Curve mg_uecc_secp224r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256r1 -MG_UECC_Curve mg_uecc_secp256r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256k1 -MG_UECC_Curve mg_uecc_secp256k1(void); -#endif -#if MG_UECC_SUPPORTS_secp384r1 -MG_UECC_Curve mg_uecc_secp384r1(void); -#endif - -/* MG_UECC_RNG_Function type -The RNG function should fill 'size' random bytes into 'dest'. It should return 1 -if 'dest' was filled with random data, or 0 if the random data could not be -generated. The filled-in values should be either truly random, or from a -cryptographically-secure PRNG. - -A correctly functioning RNG is required before calling mg_uecc_make_key() or -mg_uecc_sign(). - -Setting a correctly functioning RNG function improves the resistance to -side-channel attacks for mg_uecc_shared_secret() and -mg_uecc_sign_deterministic(). -*/ -typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size); - -// Mongoose: mg_uecc_make_key() and mg_uecc_sign() use mg_random() when no -// explicit uECC RNG is installed. If mg_random() cannot provide a strong source, -// those operations fail. Deterministic signing works without RNG, but only an -// explicit uECC RNG enables its optional side-channel blinding. - -/* mg_uecc_set_rng() function. -Set the function that will be used to generate random bytes. The RNG function -should return 1 if the random data was generated, or 0 if the random data could -not be generated. - -Mongoose: this overrides the default mg_random() fallback used by -mg_uecc_make_key() and mg_uecc_sign(). Do not call mg_uecc_set_rng() unless the -application must provide its own cryptographic RNG. - -Inputs: - rng_function - The function that will be used to generate random bytes. -*/ -void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function); - -/* mg_uecc_get_rng() function. - -Returns the function that will be used to generate random bytes. -*/ -MG_UECC_RNG_Function mg_uecc_get_rng(void); - -/* mg_uecc_curve_private_key_size() function. - -Returns the size of a private key for the curve in bytes. -*/ -int mg_uecc_curve_private_key_size(MG_UECC_Curve curve); - -/* mg_uecc_curve_public_key_size() function. - -Returns the size of a public key for the curve in bytes. -*/ -int mg_uecc_curve_public_key_size(MG_UECC_Curve curve); - -/* mg_uecc_make_key() function. -Create a public/private key pair. - -Outputs: - public_key - Will be filled in with the public key. Must be at least 2 * -the curve size (in bytes) long. For example, if the curve is secp256r1, -public_key must be 64 bytes long. private_key - Will be filled in with the -private key. Must be as long as the curve order; this is typically the same as -the curve size, except for secp160r1. For example, if the curve is secp256r1, -private_key must be 32 bytes long. - - For secp160r1, private_key must be 21 bytes long! Note that -the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it -being non-zero). - -Returns 1 if the key pair was generated successfully, 0 if an error occurred. -*/ -int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, - MG_UECC_Curve curve); - -/* mg_uecc_shared_secret() function. -Compute a shared secret given your secret key and someone else's public key. If -the public key is not from a trusted source and has not been previously -verified, you should verify it first using mg_uecc_valid_public_key(). Note: It -is recommended that you hash the result of mg_uecc_shared_secret() before using -it for symmetric encryption or HMAC. - -Inputs: - public_key - The public key of the remote party. - private_key - Your private key. - -Outputs: - secret - Will be filled in with the shared secret value. Must be the same -size as the curve size; for example, if the curve is secp256r1, secret must be -32 bytes long. - -Returns 1 if the shared secret was generated successfully, 0 if an error -occurred. -*/ -int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, - uint8_t *secret, MG_UECC_Curve curve); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* mg_uecc_compress() function. -Compress a public key. - -Inputs: - public_key - The public key to compress. - -Outputs: - compressed - Will be filled in with the compressed public key. Must be at -least (curve size + 1) bytes long; for example, if the curve is secp256r1, - compressed must be 33 bytes long. -*/ -void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, - MG_UECC_Curve curve); - -/* mg_uecc_decompress() function. -Decompress a compressed public key. - -Inputs: - compressed - The compressed public key. - -Outputs: - public_key - Will be filled in with the decompressed public key. -*/ -void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, - MG_UECC_Curve curve); -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -/* mg_uecc_valid_public_key() function. -Check to see if a public key is valid. - -Note that you are not required to check for a valid public key before using any -other uECC functions. However, you may wish to avoid spending CPU time computing -a shared secret or verifying a signature using an invalid public key. - -Inputs: - public_key - The public key to check. - -Returns 1 if the public key is valid, 0 if it is invalid. -*/ -int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve); - -/* mg_uecc_compute_public_key() function. -Compute the corresponding public key for a private key. - -Inputs: - private_key - The private key to compute the public key for - -Outputs: - public_key - Will be filled in with the corresponding public key - -Returns 1 if the key was computed successfully, 0 if an error occurred. -*/ -int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, - MG_UECC_Curve curve); - -/* mg_uecc_sign() function. -Generate an ECDSA signature for a given hash value. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it in to this function along with your private key. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - -Outputs: - signature - Will be filled in with the signature value. Must be at least 2 * -curve size long. For example, if the curve is secp256r1, signature must be 64 -bytes long. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve); - -/* MG_UECC_HashContext structure. -This is used to pass in an arbitrary hash function to -mg_uecc_sign_deterministic(). The structure will be used for multiple hash -computations; each time a new hash is computed, init_hash() will be called, -followed by one or more calls to update_hash(), and finally a call to -finish_hash() to produce the resulting hash. - -The intention is that you will create a structure that includes -MG_UECC_HashContext followed by any hash-specific data. For example: - -typedef struct SHA256_HashContext { - MG_UECC_HashContext uECC; - SHA256_CTX ctx; -} SHA256_HashContext; - -void init_SHA256(MG_UECC_HashContext *base) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Init(&context->ctx); -} - -void update_SHA256(MG_UECC_HashContext *base, - const uint8_t *message, - unsigned message_size) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Update(&context->ctx, message, message_size); -} - -void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Final(hash_result, &context->ctx); -} - -... when signing ... -{ - uint8_t tmp[32 + 32 + 64]; - SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, -32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature); -} -*/ -typedef struct MG_UECC_HashContext { - void (*init_hash)(const struct MG_UECC_HashContext *context); - void (*update_hash)(const struct MG_UECC_HashContext *context, - const uint8_t *message, unsigned message_size); - void (*finish_hash)(const struct MG_UECC_HashContext *context, - uint8_t *hash_result); - unsigned - block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned - result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + - block_size) bytes. */ -} MG_UECC_HashContext; - -/* mg_uecc_sign_deterministic() function. -Generate an ECDSA signature for a given hash value, using a deterministic -algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng() -before calling this function; however, if the RNG is defined it will improve -resistance to side-channel attacks. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it to this function along with your private key and a hash context. Note -that the message_hash does not need to be computed with the same hash function -used by hash_context. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - hash_context - A hash context to use. - -Outputs: - signature - Will be filled in with the signature value. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, unsigned hash_size, - const MG_UECC_HashContext *hash_context, - uint8_t *signature, MG_UECC_Curve curve); - -/* mg_uecc_verify() function. -Verify an ECDSA signature. - -Usage: Compute the hash of the signed data using the same hash as the signer and -pass it to this function along with the signer's public key and the signature -values (r and s). - -Inputs: - public_key - The signer's public key. - message_hash - The hash of the signed data. - hash_size - The size of message_hash in bytes. - signature - The signature value. - -Returns 1 if the signature is valid, 0 if it is invalid. -*/ -int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *signature, - MG_UECC_Curve curve); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_TYPES_H_ -#define _UECC_TYPES_H_ - -#ifndef MG_UECC_PLATFORM -#if defined(__AVR__) && __AVR__ -#define MG_UECC_PLATFORM mg_uecc_avr -#elif defined(__thumb2__) || \ - defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ -#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 -#elif defined(__thumb__) -#define MG_UECC_PLATFORM mg_uecc_arm_thumb -#elif defined(__arm__) || defined(_M_ARM) -#define MG_UECC_PLATFORM mg_uecc_arm -#elif defined(__aarch64__) -#define MG_UECC_PLATFORM mg_uecc_arm64 -#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ - defined(__I86__) -#define MG_UECC_PLATFORM mg_uecc_x86 -#elif defined(__amd64__) || defined(_M_X64) -#define MG_UECC_PLATFORM mg_uecc_x86_64 -#else -#define MG_UECC_PLATFORM mg_uecc_arch_other -#endif -#endif - -#ifndef MG_UECC_ARM_USE_UMAAL -#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) -#define MG_UECC_ARM_USE_UMAAL 1 -#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ - (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) -#define MG_UECC_ARM_USE_UMAAL 1 -#else -#define MG_UECC_ARM_USE_UMAAL 0 -#endif -#endif - -#ifndef MG_UECC_WORD_SIZE -#if MG_UECC_PLATFORM == mg_uecc_avr -#define MG_UECC_WORD_SIZE 1 -#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) -#define MG_UECC_WORD_SIZE 8 -#else -#define MG_UECC_WORD_SIZE 4 -#endif -#endif - -#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ - (MG_UECC_WORD_SIZE != 8) -#error "Unsupported value for MG_UECC_WORD_SIZE" -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) -#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 1 -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ - (MG_UECC_WORD_SIZE != 4)) -#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 4 -#endif - -typedef int8_t wordcount_t; -typedef int16_t bitcount_t; -typedef int8_t cmpresult_t; - -#if (MG_UECC_WORD_SIZE == 1) - -typedef uint8_t mg_uecc_word_t; -typedef uint16_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80 -#define MG_UECC_WORD_BITS 8 -#define MG_UECC_WORD_BITS_SHIFT 3 -#define MG_UECC_WORD_BITS_MASK 0x07 - -#elif (MG_UECC_WORD_SIZE == 4) - -typedef uint32_t mg_uecc_word_t; -typedef uint64_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80000000 -#define MG_UECC_WORD_BITS 32 -#define MG_UECC_WORD_BITS_SHIFT 5 -#define MG_UECC_WORD_BITS_MASK 0x01F - -#elif (MG_UECC_WORD_SIZE == 8) - -typedef uint64_t mg_uecc_word_t; - -#define HIGH_BIT_SET 0x8000000000000000U -#define MG_UECC_WORD_BITS 64 -#define MG_UECC_WORD_BITS_SHIFT 6 -#define MG_UECC_WORD_BITS_MASK 0x03F - -#endif /* MG_UECC_WORD_SIZE */ - -#endif /* _UECC_TYPES_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_VLI_H_ -#define _UECC_VLI_H_ - -// #include "types.h" -// #include "uECC.h" - -/* Functions for raw large-integer manipulation. These are only available - if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */ -#ifndef MG_UECC_ENABLE_VLI_API -#define MG_UECC_ENABLE_VLI_API 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_ENABLE_VLI_API - -void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, - wordcount_t num_words); - -/* Returns nonzero if bit 'bit' of vli is set. */ -mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit); - -/* Counts the number of bits required to represent vli. */ -bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, - const wordcount_t max_words); - -/* Sets dest = src. */ -void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise */ -mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns sign of left - right, in constant time. */ -cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes vli = vli >> 1. */ -void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Computes result = left + right, returning carry. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left - right, returning borrow. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left * right. Result must be 2 * num_words long. */ -void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes result = left^2. Result must be 2 * num_words long. */ -void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left, - wordcount_t num_words); - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = product % mod, where product is 2N words long. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Calculates result = product (mod curve->p), where product is up to - 2 * curve->num_words long. */ -void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, - MG_UECC_Curve curve); - -/* Computes result = (left * right) % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left * right) % curve->p. */ -void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, MG_UECC_Curve curve); - -/* Computes result = left^2 % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Computes result = left^2 % curve->p. */ -void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve); - -/* Computes result = (1 / input) % mod.*/ -void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input, - const mg_uecc_word_t *mod, wordcount_t num_words); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* Calculates a = sqrt(a) (mod curve->p) */ -void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif - -/* Converts an integer in uECC native format to big-endian bytes. */ -void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const mg_uecc_word_t *native); -/* Converts big-endian bytes to an integer in uECC native format. */ -void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes, - int num_bytes); - -unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve); - -const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve); - -int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve); - -/* Multiplies a point by a scalar. Points are represented by the X coordinate - followed by the Y coordinate in the same array, both coordinates are - curve->num_words long. Note that scalar must be curve->num_n_words long (NOT - curve->num_words). */ -void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, MG_UECC_Curve curve); - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -int mg_uecc_generate_random_int(mg_uecc_word_t *random, - const mg_uecc_word_t *top, - wordcount_t num_words); - -#endif /* MG_UECC_ENABLE_VLI_API */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_VLI_H_ */ - -// End of uecc BSD-2 diff --git a/src/tls_x25519.c b/src/tls_x25519.c deleted file mode 100644 index 68f0dca0..00000000 --- a/src/tls_x25519.c +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Adapted from STROBE: https://strobe.sourceforge.io/ - * Copyright (c) 2015-2016 Cryptography Research, Inc. - * Author: Mike Hamburg - * License: MIT License - */ -#include "tls.h" -#include "tls_x25519.h" -#include "util.h" - -#if MG_TLS == MG_TLS_BUILTIN - -const uint8_t X25519_BASE_POINT[X25519_BYTES] = {9}; - -#define X25519_WBITS 32 - -typedef uint32_t limb_t; -typedef uint64_t dlimb_t; -typedef int64_t sdlimb_t; - -#define NLIMBS (256 / X25519_WBITS) -typedef limb_t mg_fe[NLIMBS]; - -static limb_t umaal(limb_t *carry, limb_t acc, limb_t mand, limb_t mier) { - dlimb_t tmp = (dlimb_t) mand * mier + acc + *carry; - *carry = (limb_t) (tmp >> X25519_WBITS); - return (limb_t) tmp; -} - -// These functions are implemented in terms of umaal on ARM -static limb_t adc(limb_t *carry, limb_t acc, limb_t mand) { - dlimb_t total = (dlimb_t) *carry + acc + mand; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -static limb_t adc0(limb_t *carry, limb_t acc) { - dlimb_t total = (dlimb_t) *carry + acc; - *carry = (limb_t) (total >> X25519_WBITS); - return (limb_t) total; -} - -// - Precondition: carry is small. -// - Invariant: result of propagate is < 2^255 + 1 word -// - In particular, always less than 2p. -// - Also, output x >= min(x,19) -static void propagate(mg_fe x, limb_t over) { - unsigned i; - limb_t carry; - over = x[NLIMBS - 1] >> (X25519_WBITS - 1) | over << 1; - x[NLIMBS - 1] &= ~((limb_t) 1 << (X25519_WBITS - 1)); - - carry = over * 19; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry, x[i]); - } -} - -static void add(mg_fe out, const mg_fe a, const mg_fe b) { - unsigned i; - limb_t carry = 0; - for (i = 0; i < NLIMBS; i++) { - out[i] = adc(&carry, a[i], b[i]); - } - propagate(out, carry); -} - -static void sub(mg_fe out, const mg_fe a, const mg_fe b) { - unsigned i; - sdlimb_t carry = -38; - for (i = 0; i < NLIMBS; i++) { - carry = carry + a[i] - b[i]; - out[i] = (limb_t) carry; - carry >>= X25519_WBITS; - } - propagate(out, (limb_t) (1 + carry)); -} - -// `b` can contain less than 8 limbs, thus we use `limb_t *` instead of `mg_fe` -// to avoid build warnings -static void mul(mg_fe out, const mg_fe a, const limb_t *b, unsigned nb) { - limb_t accum[2 * NLIMBS] = {0}; - unsigned i, j; - - limb_t carry2; - for (i = 0; i < nb; i++) { - limb_t mand = b[i]; - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - limb_t tmp; // "a" may be misaligned - memcpy(&tmp, &a[j], sizeof(tmp)); // So make an aligned copy - accum[i + j] = umaal(&carry2, accum[i + j], mand, tmp); - } - accum[i + j] = carry2; - } - - carry2 = 0; - for (j = 0; j < NLIMBS; j++) { - out[j] = umaal(&carry2, accum[j], 38, accum[j + NLIMBS]); - } - propagate(out, carry2); -} - -static void sqr(mg_fe out, const mg_fe a) { - mul(out, a, a, NLIMBS); -} -static void mul1(mg_fe out, const mg_fe a) { - mul(out, a, out, NLIMBS); -} -static void sqr1(mg_fe a) { - mul1(a, a); -} - -static void condswap(limb_t a[2 * NLIMBS], limb_t b[2 * NLIMBS], - limb_t doswap) { - unsigned i; - for (i = 0; i < 2 * NLIMBS; i++) { - limb_t xor_ab = (a[i] ^ b[i]) & doswap; - a[i] ^= xor_ab; - b[i] ^= xor_ab; - } -} - -// Canonicalize a field element x, reducing it to the least residue which is -// congruent to it mod 2^255-19 -// - Precondition: x < 2^255 + 1 word -static limb_t canon(mg_fe x) { - // First, add 19. - unsigned i; - limb_t carry0 = 19; - limb_t res; - sdlimb_t carry; - for (i = 0; i < NLIMBS; i++) { - x[i] = adc0(&carry0, x[i]); - } - propagate(x, carry0); - - // Here, 19 <= x2 < 2^255 - // - This is because we added 19, so before propagate it can't be less - // than 19. After propagate, it still can't be less than 19, because if - // propagate does anything it adds 19. - // - We know that the high bit must be clear, because either the input was ~ - // 2^255 + one word + 19 (in which case it propagates to at most 2 words) or - // it was < 2^255. So now, if we subtract 19, we will get back to something in - // [0,2^255-19). - carry = -19; - res = 0; - for (i = 0; i < NLIMBS; i++) { - carry += x[i]; - res |= x[i] = (limb_t) carry; - carry >>= X25519_WBITS; - } - return (limb_t) (((dlimb_t) res - 1) >> X25519_WBITS); -} - -static const limb_t a24[1] = {121665}; - -static void ladder_part1(mg_fe xs[5]) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - add(t1, x2, z2); // t1 = A - sub(z2, x2, z2); // z2 = B - add(x2, x3, z3); // x2 = C - sub(z3, x3, z3); // z3 = D - mul1(z3, t1); // z3 = DA - mul1(x2, z2); // x3 = BC - add(x3, z3, x2); // x3 = DA+CB - sub(z3, z3, x2); // z3 = DA-CB - sqr1(t1); // t1 = AA - sqr1(z2); // z2 = BB - sub(x2, t1, z2); // x2 = E = AA-BB - mul(z2, x2, a24, sizeof(a24) / sizeof(a24[0])); // z2 = E*a24 - add(z2, z2, t1); // z2 = E*a24 + AA -} - -static void ladder_part2(mg_fe xs[5], const mg_fe x1) { - limb_t *x2 = xs[0], *z2 = xs[1], *x3 = xs[2], *z3 = xs[3], *t1 = xs[4]; - sqr1(z3); // z3 = (DA-CB)^2 - mul1(z3, x1); // z3 = x1 * (DA-CB)^2 - sqr1(x3); // x3 = (DA+CB)^2 - mul1(z2, x2); // z2 = AA*(E*a24+AA) - sub(x2, t1, x2); // x2 = BB again - mul1(x2, t1); // x2 = AA*BB -} - -static void x25519_core(mg_fe xs[5], const uint8_t scalar[X25519_BYTES], - const uint8_t *x1, int clamp) { - int i; - mg_fe x1_limbs; - limb_t swap = 0; - limb_t *x2 = xs[0], *x3 = xs[2], *z3 = xs[3]; - memset(xs, 0, 4 * sizeof(mg_fe)); - x2[0] = z3[0] = 1; - for (i = 0; i < NLIMBS; i++) { - x3[i] = x1_limbs[i] = - MG_U32(x1[i * 4 + 3], x1[i * 4 + 2], x1[i * 4 + 1], x1[i * 4]); - } - - for (i = 255; i >= 0; i--) { - uint8_t bytei = scalar[i / 8]; - limb_t doswap; - if (clamp) { - if (i / 8 == 0) { - bytei &= (uint8_t) ~7U; - } else if (i / 8 == X25519_BYTES - 1) { - bytei &= 0x7F; - bytei |= 0x40; - } - } - doswap = 0 - (limb_t) ((bytei >> (i % 8)) & 1); - condswap(x2, x3, swap ^ doswap); - swap = doswap; - - ladder_part1(xs); - ladder_part2(xs, (const limb_t *) x1_limbs); - } - condswap(x2, x3, swap); -} - -int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp) { - int i, ret; - mg_fe xs[5], out_limbs; - limb_t *x2, *z2, *z3, *prev; - static const struct { - uint8_t a, c, n; - } steps[13] = {{2, 1, 1}, {2, 1, 1}, {4, 2, 3}, {2, 4, 6}, {3, 1, 1}, - {3, 2, 12}, {4, 3, 25}, {2, 3, 25}, {2, 4, 50}, {3, 2, 125}, - {3, 1, 2}, {3, 1, 2}, {3, 1, 1}}; - x25519_core(xs, scalar, x1, clamp); - - // Precomputed inversion chain - x2 = xs[0]; - z2 = xs[1]; - z3 = xs[3]; - - prev = z2; - for (i = 0; i < 13; i++) { - int j; - limb_t *a = xs[steps[i].a]; - for (j = steps[i].n; j > 0; j--) { - sqr(a, prev); - prev = a; - } - mul1(a, xs[steps[i].c]); - } - - // Here prev = z3 - // x2 /= z2 - mul(out_limbs, x2, z3, NLIMBS); - ret = (int) canon(out_limbs); - if (!clamp) ret = 0; - for (i = 0; i < NLIMBS; i++) { - uint32_t n = out_limbs[i]; - out[i * 4] = (uint8_t) (n & 0xff); - out[i * 4 + 1] = (uint8_t) ((n >> 8) & 0xff); - out[i * 4 + 2] = (uint8_t) ((n >> 16) & 0xff); - out[i * 4 + 3] = (uint8_t) ((n >> 24) & 0xff); - } - return ret; -} - -#endif diff --git a/src/tls_x25519.h b/src/tls_x25519.h deleted file mode 100644 index 8addbb34..00000000 --- a/src/tls_x25519.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef TLS_X15519_H -#define TLS_X15519_H - -#include "arch.h" - -#define X25519_BYTES 32 -extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; - -int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp); - - -#endif /* TLS_X15519_H */ diff --git a/test/Makefile b/test/Makefile index 636ab9b3..eae8c1bd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -78,19 +78,10 @@ LDFLAGS += -L$(WOLFSSL)/lib -lwolfssl endif ifeq "$(SSL)" "BUILTIN" -DEFS += -DMG_TLS=MG_TLS_BUILTIN +# Small TLS records: several records per TCP segment, tests record handling +DEFS += -DMG_TLS=MG_TLS_BUILTIN -DMG_TLS_RECORD_SIZE=256 endif -tls_multirec/server: FORCE -ifneq "$(SSL)" "" -ifeq "$(MULTIREC)" "" - $(MAKE) -C tls_multirec CC=gcc -endif -endif - true - -FORCE: - true all: @@ -109,13 +100,13 @@ mongoose.c: Makefile $(wildcard ../src/*.c) $(wildcard ../src/drivers/*.c) cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ mongoose.h: $(HDRS) Makefile - cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/os_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/lfs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_x25519.h src/tls_aes128.h src/tls_uecc.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/modbus.h src/json.h src/jwt.h src/rpc.h src/dash.h src/ota.h src/health.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ + cd .. && (export LC_ALL=C ; cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/os_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/profile.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/lfs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_aes128.h src/tls_p256.h src/tls_p384.h src/tls_chacha20.h src/tls_rsa.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/modbus.h src/json.h src/jwt.h src/rpc.h src/dash.h src/ota.h src/health.h src/mdash.h src/flash.h src/wifi.h src/l2.h src/net_builtin.h src/bsd.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ # Check that all external (exported) symbols have "mg_" prefix mg_prefix: mongoose.c mongoose.h $(CC) mongoose.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T ' | grep -v 'mg_' ; test $$? = 1 -test: Makefile mongoose.h $(SRCS) tls_multirec/server +test: Makefile mongoose.h $(SRCS) $(CC) $(SRCS) $(CFLAGS) $(LDFLAGS) -o unit_test ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./unit_test @@ -265,7 +256,6 @@ packed_fs.c: Makefile data/ssi.h fuzz.c data/a.txt data/range.txt data/ca.pem ce clean: clean_tutorials clean_tutorials_embedded rm -rf $(PROG) *.exe *.o *.dSYM *_test ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* packed_fs.c pack - $(MAKE) -C tls_multirec $@ $(MAKE) -C ../resources/I-CUBE-Mongoose $@ #find tutorials -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C @@ -289,7 +279,7 @@ atcmd_test: atcmd_test.c mongoose.c mongoose.h Makefile $(CC) atcmd_test.c $(CFLAGS) $(LDFLAGS) -o $@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ -mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server +mip_tap_test: mip_x_test.c mip_tap_test.c mongoose.c mongoose.h packed_fs.c Makefile $(CC) mip_tap_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ @@ -302,7 +292,7 @@ port_tap_bridge: port_tap_bridge.c cc port_tap_bridge.c -o $@ # requires port_tap_bridge to be running -mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile tls_multirec/server +mip_port_test: mip_x_test.c mip_port_test.c mongoose.c mongoose.h packed_fs.c Makefile $(CC) mip_port_test.c packed_fs.c $(CFLAGS) $(LDFLAGS) -o $@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ diff --git a/test/mip_x_test.c b/test/mip_x_test.c index 58f63f34..03132d3a 100644 --- a/test/mip_x_test.c +++ b/test/mip_x_test.c @@ -480,8 +480,7 @@ static void test_http_server(struct mg_mgr *mgr) { } static void test_tls(struct mg_mgr *mgr) { -#if MG_TLS - char *url; +#if MG_TLS && 0 // TODO(cpq): restore multi-record TLS test char buf[FETCH_BUF_SIZE]; // make sure it can hold Makefile mg_mem_files = mg_packed_files; // Use generated packed filesystem struct mg_str data = mg_unpacked("/Makefile"); @@ -496,7 +495,8 @@ static void test_tls(struct mg_mgr *mgr) { // - Fire patched server, test multiple TLS records per TCP segment handling // skip other TLS stacks to avoid "bad client hello", we are 1.3 only if (access("tls_multirec/server", X_OK) == 0) { - url = mg_mprintf("https://%s:8443", host_ip); + char url[256]; + mg_snrintf(url, sizeof(url), "https://%s:8443", host_ip); ASSERT(system("tls_multirec/server -d tls_multirec &") == 0); sleep(1); // fetch() needs to loop enough times in order to process all TLS records; @@ -504,7 +504,6 @@ static void test_tls(struct mg_mgr *mgr) { ASSERT(fetch(mgr, buf, url, "GET /thefile HTTP/1.0\n\n") == 200); ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile ASSERT(system("killall tls_multirec/server") == 0); - free(url); } else { printf("SKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n"); } @@ -512,7 +511,6 @@ static void test_tls(struct mg_mgr *mgr) { printf("SKIPPED TLS MULTIPLE RECORDS TEST, not a known TLS 1.3 stack\n"); (void) cmpbody("", ""); (void) mgr; - (void) url; // these three: NO SIMPLE TLS TEST (void) buf; (void) data; #endif diff --git a/test/tls_multirec/Makefile b/test/tls_multirec/Makefile deleted file mode 100644 index b525c7f0..00000000 --- a/test/tls_multirec/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -CFLAGS = -g -I. # This is a known version, no need to check -CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_IPV6=1 -CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN - -server: main.c patched_mongoose.c mongoose.h Makefile - $(CC) main.c patched_mongoose.c $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) -o $@ - -clean: - rm -rf server *.exe *.o *.dSYM diff --git a/test/tls_multirec/main.c b/test/tls_multirec/main.c deleted file mode 100644 index e032a862..00000000 --- a/test/tls_multirec/main.c +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2020 Cesanta Software Limited -// All rights reserved - -#include -#include "mongoose.h" - -static int s_debug_level = MG_LL_INFO; -static const char *s_root_dir = "."; -static const char *s_addr1 = "http://0.0.0.0:8000"; -static const char *s_addr2 = "https://0.0.0.0:8443"; -static const char *s_enable_hexdump = "no"; -static const char *s_ssi_pattern = "#.html"; -static const char *s_upload_dir = NULL; // File uploads disabled by default - -// Self signed certificates, see -// https://github.com/cesanta/mongoose/blob/master/test/certs/generate.sh -#ifdef TLS_TWOWAY -static const char *s_tls_ca = - "-----BEGIN CERTIFICATE-----\n" - "MIIBFTCBvAIJAMNTFtpfcq8NMAoGCCqGSM49BAMCMBMxETAPBgNVBAMMCE1vbmdv\n" - "b3NlMB4XDTI0MDUwNzE0MzczNloXDTM0MDUwNTE0MzczNlowEzERMA8GA1UEAwwI\n" - "TW9uZ29vc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASuP+86T/rOWnGpEVhl\n" - "fxYZ+pjMbCmDZ+vdnP0rjoxudwRMRQCv5slRlDK7Lxue761sdvqxWr0Ma6TFGTNg\n" - "epsRMAoGCCqGSM49BAMCA0gAMEUCIQCwb2CxuAKm51s81S6BIoy1IcandXSohnqs\n" - "us64BAA7QgIgGGtUrpkgFSS0oPBlCUG6YPHFVw42vTfpTC0ySwAS0M4=\n" - "-----END CERTIFICATE-----\n"; -#endif -static const char *s_tls_cert = - "-----BEGIN CERTIFICATE-----\n" - "MIIBMTCB2aADAgECAgkAluqkgeuV/zUwCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI\n" - "TW9uZ29vc2UwHhcNMjQwNTA3MTQzNzM2WhcNMzQwNTA1MTQzNzM2WjARMQ8wDQYD\n" - "VQQDDAZzZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASo3oEiG+BuTt5y\n" - "ZRyfwNr0C+SP+4M0RG2pYkb2v+ivbpfi72NHkmXiF/kbHXtgmSrn/PeTqiA8M+mg\n" - "BhYjDX+zoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwIDRwAw\n" - "RAIgTXW9MITQSwzqbNTxUUdt9DcB+8pPUTbWZpiXcA26GMYCIBiYw+DSFMLHmkHF\n" - "+5U3NXW3gVCLN9ntD5DAx8LTG8sB\n" - "-----END CERTIFICATE-----\n"; - -static const char *s_tls_key = - "-----BEGIN EC PRIVATE KEY-----\n" - "MHcCAQEEIAVdo8UAScxG7jiuNY2UZESNX/KPH8qJ0u0gOMMsAzYWoAoGCCqGSM49\n" - "AwEHoUQDQgAEqN6BIhvgbk7ecmUcn8Da9Avkj/uDNERtqWJG9r/or26X4u9jR5Jl\n" - "4hf5Gx17YJkq5/z3k6ogPDPpoAYWIw1/sw==\n" - "-----END EC PRIVATE KEY-----\n"; - -// Handle interrupts, like Ctrl-C -static int s_signo; -static void signal_handler(int signo) { - s_signo = signo; -} - -// Event handler for the listening connection. -// Simply serve static files from `s_root_dir` -static void cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_ACCEPT && c->fn_data != NULL) { - struct mg_tls_opts opts; - memset(&opts, 0, sizeof(opts)); -#ifdef TLS_TWOWAY - opts.ca = mg_str(s_tls_ca); -#endif - opts.cert = mg_str(s_tls_cert); - opts.key = mg_str(s_tls_key); - mg_tls_init(c, &opts); - } - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - - if (mg_match(hm->uri, mg_str("/upload"), NULL)) { - // Serve file upload - if (s_upload_dir == NULL) { - mg_http_reply(c, 403, "", "Denied: file upload directory not set\n"); - } else { - struct mg_http_part part; - size_t pos = 0, total_bytes = 0, num_files = 0; - while ((pos = mg_http_next_multipart(hm->body, pos, &part)) > 0) { - char path[MG_PATH_MAX]; - MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes", - part.name.len, part.name.buf, part.filename.len, - part.filename.buf, part.body.len)); - mg_snprintf(path, sizeof(path), "%s/%.*s", s_upload_dir, - part.filename.len, part.filename.buf); - if (mg_path_is_sane(mg_str(path))) { - mg_file_write(&mg_fs_posix, path, part.body.buf, part.body.len); - total_bytes += part.body.len; - num_files++; - } else { - MG_ERROR(("Rejecting dangerous path %s", path)); - } - } - mg_http_reply(c, 200, "", "Uploaded %lu files, %lu bytes\n", num_files, - total_bytes); - } - } else { - // Serve web root directory - struct mg_http_serve_opts opts = {0}; - opts.root_dir = s_root_dir; - opts.ssi_pattern = s_ssi_pattern; - mg_http_serve_dir(c, hm, &opts); - } - - // Log request - MG_INFO(("%.*s %.*s %lu -> %.*s %lu", hm->method.len, hm->method.buf, - hm->uri.len, hm->uri.buf, hm->body.len, 3, c->send.buf + 9, - c->send.len)); - } -} - -static void usage(const char *prog) { - fprintf(stderr, - "Mongoose v.%s\n" - "Usage: %s OPTIONS\n" - " -H yes|no - enable traffic hexdump, default: '%s'\n" - " -S PAT - SSI filename pattern, default: '%s'\n" - " -d DIR - directory to serve, default: '%s'\n" - " -l ADDR - listening address, default: '%s'\n" - " -u DIR - file upload directory, default: unset\n" - " -v LEVEL - debug level, from 0 to 4, default: %d\n", - MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir, - s_addr1, s_debug_level); - exit(EXIT_FAILURE); -} - -int main(int argc, char *argv[]) { - char path[MG_PATH_MAX] = "."; - struct mg_mgr mgr; - struct mg_connection *c; - int i; - - // Parse command-line flags - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-d") == 0) { - s_root_dir = argv[++i]; - } else if (strcmp(argv[i], "-H") == 0) { - s_enable_hexdump = argv[++i]; - } else if (strcmp(argv[i], "-S") == 0) { - s_ssi_pattern = argv[++i]; - } else if (strcmp(argv[i], "-l") == 0) { - s_addr1 = argv[++i]; - } else if (strcmp(argv[i], "-l2") == 0) { - s_addr2 = argv[++i]; - } else if (strcmp(argv[i], "-u") == 0) { - s_upload_dir = argv[++i]; - } else if (strcmp(argv[i], "-v") == 0) { - s_debug_level = atoi(argv[++i]); - } else { - usage(argv[0]); - } - } - - // Root directory must not contain double dots. Make it absolute - // Do the conversion only if the root dir spec does not contain overrides - if (strchr(s_root_dir, ',') == NULL) { - realpath(s_root_dir, path); - s_root_dir = path; - } - - // Initialise stuff - signal(SIGINT, signal_handler); - signal(SIGTERM, signal_handler); - mg_log_set(s_debug_level); - mg_mgr_init(&mgr); - if ((c = mg_http_listen(&mgr, s_addr1, cb, NULL)) == NULL) { - MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT", - s_addr1)); - exit(EXIT_FAILURE); - } - if ((c = mg_http_listen(&mgr, s_addr2, cb, (void *) 1)) == NULL) { - MG_ERROR(("Cannot listen on %s. Use http://ADDR:PORT or :PORT", - s_addr2)); - exit(EXIT_FAILURE); - } - if (mg_casecmp(s_enable_hexdump, "yes") == 0) c->is_hexdumping = 1; - - // Start infinite event loop - MG_INFO(("Mongoose version : v%s", MG_VERSION)); - MG_INFO(("HTTP listener : %s", s_addr1)); - MG_INFO(("HTTPS listener : %s", s_addr2)); - MG_INFO(("Web root : [%s]", s_root_dir)); - MG_INFO(("Upload dir : [%s]", s_upload_dir ? s_upload_dir : "unset")); - while (s_signo == 0) mg_mgr_poll(&mgr, 1000); - mg_mgr_free(&mgr); - MG_INFO(("Exiting on signal %d", s_signo)); - return 0; -} diff --git a/test/tls_multirec/mongoose.h b/test/tls_multirec/mongoose.h deleted file mode 100644 index 571cae71..00000000 --- a/test/tls_multirec/mongoose.h +++ /dev/null @@ -1,3282 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2024 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see http://www.gnu.org/licenses/ -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in https://www.mongoose.ws/licensing/ -// -// SPDX-License-Identifier: GPL-2.0-only or commercial - -#ifndef MONGOOSE_H -#define MONGOOSE_H - -#define MG_VERSION "7.16" - -#ifdef __cplusplus -extern "C" { -#endif - - -#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_config.h -#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ... -#define MG_ARCH_WIN32 2 // Windows -#define MG_ARCH_ESP32 3 // ESP32 -#define MG_ARCH_ESP8266 4 // ESP8266 -#define MG_ARCH_FREERTOS 5 // FreeRTOS -#define MG_ARCH_AZURERTOS 6 // MS Azure RTOS -#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS -#define MG_ARCH_ARMGCC 8 // Bare metal ARM -#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX) -#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS -#define MG_ARCH_PICOSDK 11 // Raspberry Pi Pico-SDK (RP2040, RP2350) -#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard -#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS) -#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS -#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT - -#if !defined(MG_ARCH) -#if defined(__unix__) || defined(__APPLE__) -#define MG_ARCH MG_ARCH_UNIX -#elif defined(_WIN32) -#define MG_ARCH MG_ARCH_WIN32 -#endif -#endif // !defined(MG_ARCH) - -#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM) -#include "mongoose_config.h" // keep this include -#endif - -#if !defined(MG_ARCH) -#error "MG_ARCH is not specified and we couldn't guess it. Define MG_ARCH=... in your compiler" -#endif - -// http://esr.ibiblio.org/?p=5095 -#define MG_BIG_ENDIAN (*(uint16_t *) "\0\xff" < 0x100) - - - - - - - - - - - - - - - -#if MG_ARCH == MG_ARCH_ARMCGT - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MG_PATH_MAX 100 -#define MG_ENABLE_SOCKET 0 -#define MG_ENABLE_DIRLIST 0 - -#endif - - -#if MG_ARCH == MG_ARCH_AZURERTOS - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#define PATH_MAX FX_MAXIMUM_PATH -#define MG_DIRSEP '\\' - -#define socklen_t int -#define closesocket(x) soc_close(x) - -#undef FOPEN_MAX - -#endif - - -#if MG_ARCH == MG_ARCH_ESP32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // Use angle brackets to avoid -#include // amalgamation ditching them - -#define MG_PATH_MAX 128 - -#endif - - -#if MG_ARCH == MG_ARCH_ESP8266 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MG_PATH_MAX 128 - -#endif - - -#if MG_ARCH == MG_ARCH_FREERTOS - -#include -#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP -#include -#endif -#include -#include -#include -#include -#include -#include // rand(), strtol(), atoi() -#include -#if defined(__ARMCC_VERSION) -#define mode_t size_t -#include -#include -#elif defined(__CCRH__) -#else -#include -#endif - -#include -#include - -#define calloc(a, b) mg_calloc(a, b) -#define free(a) vPortFree(a) -#define malloc(a) pvPortMalloc(a) -#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) - -// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib -static inline void *mg_calloc(size_t cnt, size_t size) { - void *p = pvPortMalloc(cnt * size); - if (p != NULL) memset(p, 0, size * cnt); - return p; -} - -#define mkdir(a, b) mg_mkdir(a, b) -static inline int mg_mkdir(const char *path, mode_t mode) { - (void) path, (void) mode; - return -1; -} - -#endif // MG_ARCH == MG_ARCH_FREERTOS - - -#if MG_ARCH == MG_ARCH_ARMGCC -#define _POSIX_TIMERS - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MG_PATH_MAX 100 -#define MG_ENABLE_SOCKET 0 -#define MG_ENABLE_DIRLIST 0 - -#endif - - -#if MG_ARCH == MG_ARCH_PICOSDK -#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#include -#include -int mkdir(const char *, mode_t); - -#if MG_OTA == MG_OTA_PICOSDK -#include -#if PICO_RP2040 -#include -#endif -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_RTTHREAD - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 1460 -#endif - -#endif // MG_ARCH == MG_ARCH_RTTHREAD - - -#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \ - MG_ARCH == MG_ARCH_CMSIS_RTOS2 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if MG_ARCH == MG_ARCH_CMSIS_RTOS1 -#include "cmsis_os.h" // keep this include -// https://developer.arm.com/documentation/ka003821/latest -extern uint32_t rt_time_get(void); -#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2 -#include "cmsis_os2.h" // keep this include -#endif - -#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) - -#if defined(__ARMCC_VERSION) -#define mode_t size_t -#define mkdir(a, b) mg_mkdir(a, b) -static inline int mg_mkdir(const char *path, mode_t mode) { - (void) path, (void) mode; - return -1; -} -#endif - -#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \ - !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \ - (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP) -#define MG_ENABLE_RL 1 -#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE -#define MG_SOCK_LISTEN_BACKLOG_SIZE 3 -#endif -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_TIRTOS - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#endif - - -#if MG_ARCH == MG_ARCH_UNIX - -#define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors - -#if defined(__APPLE__) -#include -#endif - -#if !defined(MG_ENABLE_EPOLL) && defined(__linux__) -#define MG_ENABLE_EPOLL 1 -#elif !defined(MG_ENABLE_POLL) -#define MG_ENABLE_POLL 1 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL -#include -#elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 1 -#endif - -#ifndef MG_PATH_MAX -#define MG_PATH_MAX FILENAME_MAX -#endif - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 1 -#endif - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 16384 -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_WIN32 - -#ifndef _CRT_RAND_S -#define _CRT_RAND_S -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif - -#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS -#define _WINSOCK_DEPRECATED_NO_WARNINGS -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1700 -#define __func__ "" -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -typedef unsigned char uint8_t; -typedef char int8_t; -typedef unsigned short uint16_t; -typedef short int16_t; -typedef unsigned int uint32_t; -typedef int int32_t; -typedef enum { false = 0, true = 1 } bool; -#else -#include -#include -#include -#endif - -#include -#include -#include - -// For mg_random() -#if defined(_MSC_VER) && _MSC_VER < 1700 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x400 // Let vc98 pick up wincrypt.h -#endif -#include -#pragma comment(lib, "advapi32.lib") -#endif - -// Protect from calls like std::snprintf in app code -// See https://github.com/cesanta/mongoose/issues/1047 -#ifndef __cplusplus -#define snprintf _snprintf -#define vsnprintf _vsnprintf -#ifndef strdup // For MSVC with _DEBUG, see #1359 -#define strdup(x) _strdup(x) -#endif -#endif - -#define MG_INVALID_SOCKET INVALID_SOCKET -#define MG_SOCKET_TYPE SOCKET -typedef unsigned long nfds_t; -#if defined(_MSC_VER) -#pragma comment(lib, "ws2_32.lib") -#ifndef alloca -#define alloca(a) _alloca(a) -#endif -#endif -#define poll(a, b, c) WSAPoll((a), (b), (c)) -#define closesocket(x) closesocket(x) - -typedef int socklen_t; -#define MG_DIRSEP '\\' - -#ifndef MG_PATH_MAX -#define MG_PATH_MAX FILENAME_MAX -#endif - -#ifndef SO_EXCLUSIVEADDRUSE -#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR)) -#endif - -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? WSAGetLastError() : 0) - -#define MG_SOCK_PENDING(errcode) \ - (((errcode) < 0) && \ - (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || \ - WSAGetLastError() == WSAEWOULDBLOCK)) - -#define MG_SOCK_RESET(errcode) \ - (((errcode) < 0) && (WSAGetLastError() == WSAECONNRESET)) - -#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX) -#define sleep(x) Sleep((x) * 1000) -#define mkdir(a, b) _mkdir(a) -#define timegm(x) _mkgmtime(x) - -#ifndef S_ISDIR -#define S_ISDIR(x) (((x) & _S_IFMT) == _S_IFDIR) -#endif - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 1 -#endif - -#ifndef SIGPIPE -#define SIGPIPE 0 -#endif - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 1 -#endif - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 16384 -#endif - -#endif - - -#if MG_ARCH == MG_ARCH_ZEPHYR - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MG_PUTCHAR(x) printk("%c", x) -#ifndef strdup -#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf) -#endif -#define strerror(x) zsock_gai_strerror(x) - -#ifndef FD_CLOEXEC -#define FD_CLOEXEC 0 -#endif - -#ifndef F_SETFD -#define F_SETFD 0 -#endif - -#define MG_ENABLE_SSI 0 - -int rand(void); -int sscanf(const char *, const char *, ...); - -#endif - - -#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP - -#include -#include - -#include -#include - -#define MG_SOCKET_TYPE Socket_t -#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET - -// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing -// with FreeRTOS_ prefix, is beyond me -#define IPPROTO_TCP FREERTOS_IPPROTO_TCP -#define IPPROTO_UDP FREERTOS_IPPROTO_UDP -#define AF_INET FREERTOS_AF_INET -#define SOCK_STREAM FREERTOS_SOCK_STREAM -#define SOCK_DGRAM FREERTOS_SOCK_DGRAM -#define SO_BROADCAST 0 -#define SO_ERROR 0 -#define SOL_SOCKET 0 -#define SO_REUSEADDR 0 - -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0) - -#define MG_SOCK_PENDING(errcode) \ - ((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \ - (errcode) == -pdFREERTOS_ERRNO_EISCONN || \ - (errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \ - (errcode) == -pdFREERTOS_ERRNO_EAGAIN) - -#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN) - -// actually only if optional timeout is enabled -#define MG_SOCK_INTR(fd) (fd == NULL) - -#define sockaddr_in freertos_sockaddr -#define sockaddr freertos_sockaddr -#if ipFR_TCP_VERSION_MAJOR >= 4 -#define sin_addr sin_address.ulIP_IPv4 -#endif -#define accept(a, b, c) FreeRTOS_accept((a), (b), (c)) -#define connect(a, b, c) FreeRTOS_connect((a), (b), (c)) -#define bind(a, b, c) FreeRTOS_bind((a), (b), (c)) -#define listen(a, b) FreeRTOS_listen((a), (b)) -#define socket(a, b, c) FreeRTOS_socket((a), (b), (c)) -#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d)) -#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d)) -#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e)) -#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f)) -#define recvfrom(a, b, c, d, e, f) \ - FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f)) -#define closesocket(x) FreeRTOS_closesocket(x) -#define gethostbyname(x) FreeRTOS_gethostbyname(x) -#define getsockname(a, b, c) mg_getsockname((a), (b), (c)) -#define getpeername(a, b, c) mg_getpeername((a), (b), (c)) - -static inline int mg_getsockname(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) { - (void) fd, (void) buf, (void) len; - return -1; -} - -static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) { - (void) fd, (void) buf, (void) len; - return 0; -} -#endif - - -#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP - -#if defined(__GNUC__) && !defined(__ARMCC_VERSION) -#include -#endif - -struct timeval; - -#include - -#if !LWIP_TIMEVAL_PRIVATE -#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both -#include -#else -struct timeval { - time_t tv_sec; - long tv_usec; -}; -#endif -#endif - -#if LWIP_SOCKET != 1 -// Sockets support disabled in LWIP by default -#error Set LWIP_SOCKET variable to 1 (in lwipopts.h) -#endif -#endif - - -#if defined(MG_ENABLE_RL) && MG_ENABLE_RL -#include - -#define closesocket(x) closesocket(x) - -#define TCP_NODELAY SO_KEEPALIVE - -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0) - -#define MG_SOCK_PENDING(errcode) \ - ((errcode) == BSD_EWOULDBLOCK || (errcode) == BSD_EALREADY || \ - (errcode) == BSD_EINPROGRESS) - -#define MG_SOCK_RESET(errcode) \ - ((errcode) == BSD_ECONNABORTED || (errcode) == BSD_ECONNRESET) - -// In blocking mode, which is enabled by default, accept() waits for a -// connection request. In non blocking mode, you must call accept() -// again if the error code BSD_EWOULDBLOCK is returned. -#define MG_SOCK_INTR(fd) (fd == BSD_EWOULDBLOCK) - -#define socklen_t int -#endif - - -#ifndef MG_ENABLE_LOG -#define MG_ENABLE_LOG 1 -#endif - -#ifndef MG_ENABLE_CUSTOM_LOG -#define MG_ENABLE_CUSTOM_LOG 0 // Let user define their own MG_LOG -#endif - -#ifndef MG_ENABLE_TCPIP -#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack -#endif - -#ifndef MG_ENABLE_LWIP -#define MG_ENABLE_LWIP 0 // lWIP network stack -#endif - -#ifndef MG_ENABLE_FREERTOS_TCP -#define MG_ENABLE_FREERTOS_TCP 0 // Amazon FreeRTOS-TCP network stack -#endif - -#ifndef MG_ENABLE_RL -#define MG_ENABLE_RL 0 // ARM MDK network stack -#endif - -#ifndef MG_ENABLE_SOCKET -#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP -#endif - -#ifndef MG_ENABLE_POLL -#define MG_ENABLE_POLL 0 -#endif - -#ifndef MG_ENABLE_EPOLL -#define MG_ENABLE_EPOLL 0 -#endif - -#ifndef MG_ENABLE_FATFS -#define MG_ENABLE_FATFS 0 -#endif - -#ifndef MG_ENABLE_SSI -#define MG_ENABLE_SSI 0 -#endif - -#ifndef MG_ENABLE_IPV6 -#define MG_ENABLE_IPV6 0 -#endif - -#ifndef MG_IPV6_V6ONLY -#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address -#endif - -#ifndef MG_ENABLE_MD5 -#define MG_ENABLE_MD5 1 -#endif - -// Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP) -#ifndef MG_ENABLE_WINSOCK -#define MG_ENABLE_WINSOCK 1 -#endif - -#ifndef MG_ENABLE_DIRLIST -#define MG_ENABLE_DIRLIST 0 -#endif - -#ifndef MG_ENABLE_CUSTOM_RANDOM -#define MG_ENABLE_CUSTOM_RANDOM 0 -#endif - -#ifndef MG_ENABLE_CUSTOM_MILLIS -#define MG_ENABLE_CUSTOM_MILLIS 0 -#endif - -#ifndef MG_ENABLE_PACKED_FS -#define MG_ENABLE_PACKED_FS 0 -#endif - -#ifndef MG_ENABLE_ASSERT -#define MG_ENABLE_ASSERT 0 -#endif - -#ifndef MG_IO_SIZE -#define MG_IO_SIZE 512 // Granularity of the send/recv IO buffer growth -#endif - -#ifndef MG_MAX_RECV_SIZE -#define MG_MAX_RECV_SIZE (3UL * 1024UL * 1024UL) // Maximum recv IO buffer size -#endif - -#ifndef MG_DATA_SIZE -#define MG_DATA_SIZE 32 // struct mg_connection :: data size -#endif - -#ifndef MG_MAX_HTTP_HEADERS -#define MG_MAX_HTTP_HEADERS 30 -#endif - -#ifndef MG_HTTP_INDEX -#define MG_HTTP_INDEX "index.html" -#endif - -#ifndef MG_PATH_MAX -#ifdef PATH_MAX -#define MG_PATH_MAX PATH_MAX -#else -#define MG_PATH_MAX 128 -#endif -#endif - -#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE -#define MG_SOCK_LISTEN_BACKLOG_SIZE 128 -#endif - -#ifndef MG_DIRSEP -#define MG_DIRSEP '/' -#endif - -#ifndef MG_ENABLE_POSIX_FS -#define MG_ENABLE_POSIX_FS 0 -#endif - -#ifndef MG_INVALID_SOCKET -#define MG_INVALID_SOCKET (-1) -#endif - -#ifndef MG_SOCKET_TYPE -#define MG_SOCKET_TYPE int -#endif - -#ifndef MG_SOCKET_ERRNO -#define MG_SOCKET_ERRNO errno -#endif - -#if MG_ENABLE_EPOLL -#define MG_EPOLL_ADD(c) \ - do { \ - struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \ - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int) (size_t) c->fd, &ev); \ - } while (0) -#define MG_EPOLL_MOD(c, wr) \ - do { \ - struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \ - if (wr) ev.events |= EPOLLOUT; \ - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int) (size_t) c->fd, &ev); \ - } while (0) -#else -#define MG_EPOLL_ADD(c) -#define MG_EPOLL_MOD(c, wr) -#endif - -#ifndef MG_ENABLE_PROFILE -#define MG_ENABLE_PROFILE 0 -#endif - -#ifndef MG_ENABLE_TCPIP_DRIVER_INIT // mg_mgr_init() will also initialize -#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for -#endif // Mongoose built-in network stack - -#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223) -#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) -#endif - -#ifndef MG_TCPIP_MASK -#define MG_TCPIP_MASK MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) -#endif - -#ifndef MG_TCPIP_GW -#define MG_TCPIP_GW MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP) -#endif - -#ifndef MG_SET_MAC_ADDRESS -#define MG_SET_MAC_ADDRESS(mac) -#endif - -#ifndef MG_SET_WIFI_CREDS -#define MG_SET_WIFI_CREDS(ssid, pass) -#endif - -#ifndef MG_ENABLE_TCPIP_PRINT_DEBUG_STATS -#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0 -#endif - - - - -// Describes an arbitrary chunk of memory -struct mg_str { - char *buf; // String data - size_t len; // String length -}; - -// Using macro to avoid shadowing C++ struct constructor, see #1298 -#define mg_str(s) mg_str_s(s) - -struct mg_str mg_str(const char *s); -struct mg_str mg_str_n(const char *s, size_t n); -int mg_casecmp(const char *s1, const char *s2); -int mg_strcmp(const struct mg_str str1, const struct mg_str str2); -int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2); -struct mg_str mg_strdup(const struct mg_str s); -bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps); -bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char delim); - -bool mg_str_to_num(struct mg_str, int base, void *val, size_t val_len); - - - - -// Single producer, single consumer non-blocking queue - -struct mg_queue { - char *buf; - size_t size; - volatile size_t tail; - volatile size_t head; -}; - -void mg_queue_init(struct mg_queue *, char *, size_t); // Init queue -size_t mg_queue_book(struct mg_queue *, char **buf, size_t); // Reserve space -void mg_queue_add(struct mg_queue *, size_t); // Add new message -size_t mg_queue_next(struct mg_queue *, char **); // Get oldest message -void mg_queue_del(struct mg_queue *, size_t); // Delete oldest message - - - - -typedef void (*mg_pfn_t)(char, void *); // Output function -typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer - -size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *); -size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...); - - - - - - -// Convenience wrappers around mg_xprintf -size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap); -size_t mg_snprintf(char *, size_t, const char *fmt, ...); -char *mg_vmprintf(const char *fmt, va_list *ap); -char *mg_mprintf(const char *fmt, ...); -size_t mg_queue_vprintf(struct mg_queue *, const char *fmt, va_list *); -size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...); - -// %M print helper functions -size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap); -size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap); - -// Various output functions -void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * -void mg_pfn_stdout(char c, void *param); // param: ignored - -// A helper macro for printing JSON: mg_snprintf(buf, len, "%m", MG_ESC("hi")) -#define MG_ESC(str) mg_print_esc, 0, (str) - - - - - - -enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE }; -extern int mg_log_level; // Current log level, one of MG_LL_* - -void mg_log(const char *fmt, ...); -void mg_log_prefix(int ll, const char *file, int line, const char *fname); -// bool mg_log2(int ll, const char *file, int line, const char *fmt, ...); -void mg_hexdump(const void *buf, size_t len); -void mg_log_set_fn(mg_pfn_t fn, void *param); - -#define mg_log_set(level_) mg_log_level = (level_) - -#if MG_ENABLE_LOG -#define MG_LOG(level, args) \ - do { \ - if ((level) <= mg_log_level) { \ - mg_log_prefix((level), __FILE__, __LINE__, __func__); \ - mg_log args; \ - } \ - } while (0) -#else -#define MG_LOG(level, args) \ - do { \ - if (0) mg_log args; \ - } while (0) -#endif - -#define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args) -#define MG_INFO(args) MG_LOG(MG_LL_INFO, args) -#define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args) -#define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args) - - - - -struct mg_timer { - unsigned long id; // Timer ID - uint64_t period_ms; // Timer period in milliseconds - uint64_t expire; // Expiration timestamp in milliseconds - unsigned flags; // Possible flags values below -#define MG_TIMER_ONCE 0 // Call function once -#define MG_TIMER_REPEAT 1 // Call function periodically -#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set - void (*fn)(void *); // Function to call - void *arg; // Function argument - struct mg_timer *next; // Linkage -}; - -void mg_timer_init(struct mg_timer **head, struct mg_timer *timer, - uint64_t milliseconds, unsigned flags, void (*fn)(void *), - void *arg); -void mg_timer_free(struct mg_timer **head, struct mg_timer *); -void mg_timer_poll(struct mg_timer **head, uint64_t new_ms); -bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now); - - - - - -enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 }; - -// Filesystem API functions -// st() returns MG_FS_* flags and populates file size and modification time -// ls() calls fn() for every directory entry, allowing to list a directory -// -// NOTE: UNIX-style shorthand names for the API functions are deliberately -// chosen to avoid conflicts with some libraries that make macros for e.g. -// stat(), write(), read() calls. -struct mg_fs { - int (*st)(const char *path, size_t *size, time_t *mtime); // stat file - void (*ls)(const char *path, void (*fn)(const char *, void *), - void *); // List directory entries: call fn(file_name, fn_data) - // for each directory entry - void *(*op)(const char *path, int flags); // Open file - void (*cl)(void *fd); // Close file - size_t (*rd)(void *fd, void *buf, size_t len); // Read file - size_t (*wr)(void *fd, const void *buf, size_t len); // Write file - size_t (*sk)(void *fd, size_t offset); // Set file position - bool (*mv)(const char *from, const char *to); // Rename file - bool (*rm)(const char *path); // Delete file - bool (*mkd)(const char *path); // Create directory -}; - -extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek -extern struct mg_fs mg_fs_packed; // Packed FS, see examples/device-dashboard -extern struct mg_fs mg_fs_fat; // FAT FS - -// File descriptor -struct mg_fd { - void *fd; - struct mg_fs *fs; -}; - -struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags); -void mg_fs_close(struct mg_fd *fd); -bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len); -struct mg_str mg_file_read(struct mg_fs *fs, const char *path); -bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t); -bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...); - -// Packed API -const char *mg_unpack(const char *path, size_t *size, time_t *mtime); -const char *mg_unlist(size_t no); // Get no'th packed filename -struct mg_str mg_unpacked(const char *path); // Packed file as mg_str - - - - - - - -#if MG_ENABLE_ASSERT -#include -#elif !defined(assert) -#define assert(x) -#endif - -void mg_bzero(volatile unsigned char *buf, size_t len); -bool mg_random(void *buf, size_t len); -char *mg_random_str(char *buf, size_t len); -uint16_t mg_ntohs(uint16_t net); -uint32_t mg_ntohl(uint32_t net); -uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len); -uint64_t mg_millis(void); // Return milliseconds since boot -bool mg_path_is_sane(const struct mg_str path); - -#define mg_htons(x) mg_ntohs(x) -#define mg_htonl(x) mg_ntohl(x) - -#define MG_U32(a, b, c, d) \ - (((uint32_t) ((a) & 255) << 24) | ((uint32_t) ((b) & 255) << 16) | \ - ((uint32_t) ((c) & 255) << 8) | (uint32_t) ((d) & 255)) - -#define MG_IPV4(a, b, c, d) mg_htonl(MG_U32(a, b, c, d)) - -// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip)) -#define MG_U8P(ADDR) ((uint8_t *) (ADDR)) -#define MG_IPADDR_PARTS(ADDR) \ - MG_U8P(ADDR)[0], MG_U8P(ADDR)[1], MG_U8P(ADDR)[2], MG_U8P(ADDR)[3] - -#define MG_LOAD_BE16(p) ((uint16_t) ((MG_U8P(p)[0] << 8U) | MG_U8P(p)[1])) -#define MG_LOAD_BE24(p) \ - ((uint32_t) ((MG_U8P(p)[0] << 16U) | (MG_U8P(p)[1] << 8U) | MG_U8P(p)[2])) -#define MG_STORE_BE16(p, n) \ - do { \ - MG_U8P(p)[0] = ((n) >> 8U) & 255; \ - MG_U8P(p)[1] = (n) &255; \ - } while (0) - -#define MG_REG(x) ((volatile uint32_t *) (x))[0] -#define MG_BIT(x) (((uint32_t) 1U) << (x)) -#define MG_SET_BITS(R, CLRMASK, SETMASK) (R) = ((R) & ~(CLRMASK)) | (SETMASK) - -#define MG_ROUND_UP(x, a) ((a) == 0 ? (x) : ((((x) + (a) -1) / (a)) * (a))) -#define MG_ROUND_DOWN(x, a) ((a) == 0 ? (x) : (((x) / (a)) * (a))) - -#if defined(__GNUC__) -#define MG_ARM_DISABLE_IRQ() asm volatile("cpsid i" : : : "memory") -#define MG_ARM_ENABLE_IRQ() asm volatile("cpsie i" : : : "memory") -#elif defined(__CCRH__) -#define MG_RH850_DISABLE_IRQ() __DI() -#define MG_RH850_ENABLE_IRQ() __EI() -#else -#define MG_ARM_DISABLE_IRQ() -#define MG_ARM_ENABLE_IRQ() -#endif - -#if defined(__CC_ARM) -#define MG_DSB() __dsb(0xf) -#elif defined(__ARMCC_VERSION) -#define MG_DSB() __builtin_arm_dsb(0xf) -#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__) -#define MG_DSB() asm("DSB 0xf") -#elif defined(__ICCARM__) -#define MG_DSB() __iar_builtin_DSB() -#else -#define MG_DSB() -#endif - -struct mg_addr; -int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip); - -// Linked list management macros -#define LIST_ADD_HEAD(type_, head_, elem_) \ - do { \ - (elem_)->next = (*head_); \ - *(head_) = (elem_); \ - } while (0) - -#define LIST_ADD_TAIL(type_, head_, elem_) \ - do { \ - type_ **h = head_; \ - while (*h != NULL) h = &(*h)->next; \ - *h = (elem_); \ - } while (0) - -#define LIST_DELETE(type_, head_, elem_) \ - do { \ - type_ **h = head_; \ - while (*h != (elem_)) h = &(*h)->next; \ - *h = (elem_)->next; \ - } while (0) - - - -unsigned short mg_url_port(const char *url); -int mg_url_is_ssl(const char *url); -struct mg_str mg_url_host(const char *url); -struct mg_str mg_url_user(const char *url); -struct mg_str mg_url_pass(const char *url); -const char *mg_url_uri(const char *url); - - - - -struct mg_iobuf { - unsigned char *buf; // Pointer to stored data - size_t size; // Total size available - size_t len; // Current number of bytes - size_t align; // Alignment during allocation -}; - -int mg_iobuf_init(struct mg_iobuf *, size_t, size_t); -int mg_iobuf_resize(struct mg_iobuf *, size_t); -void mg_iobuf_free(struct mg_iobuf *); -size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t); -size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len); - - -size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len); -size_t mg_base64_final(char *buf, size_t len); -size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t); -size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t); - - - - -typedef struct { - uint32_t buf[4]; - uint32_t bits[2]; - unsigned char in[64]; -} mg_md5_ctx; - -void mg_md5_init(mg_md5_ctx *c); -void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len); -void mg_md5_final(mg_md5_ctx *c, unsigned char[16]); - - - - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} mg_sha1_ctx; - -void mg_sha1_init(mg_sha1_ctx *); -void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len); -void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *); -// https://github.com/B-Con/crypto-algorithms -// Author: Brad Conte (brad AT bradconte.com) -// Disclaimer: This code is presented "as is" without any guarantees. -// Details: Defines the API for the corresponding SHA1 implementation. -// Copyright: public domain - - - - - -typedef struct { - uint32_t state[8]; - uint64_t bits; - uint32_t len; - unsigned char buffer[64]; -} mg_sha256_ctx; - -void mg_sha256_init(mg_sha256_ctx *); -void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len); -void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *); -void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, - size_t datasz); -#ifndef TLS_X15519_H -#define TLS_X15519_H - - - -#define X25519_BYTES 32 -extern const uint8_t X25519_BASE_POINT[X25519_BYTES]; - -int mg_tls_x25519(uint8_t out[X25519_BYTES], const uint8_t scalar[X25519_BYTES], - const uint8_t x1[X25519_BYTES], int clamp); - - -#endif /* TLS_X15519_H */ -/****************************************************************************** - * - * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL - * - * This is a simple and straightforward implementation of AES-GCM authenticated - * encryption. The focus of this work was correctness & accuracy. It is written - * in straight 'C' without any particular focus upon optimization or speed. It - * should be endian (memory byte order) neutral since the few places that care - * are handled explicitly. - * - * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. - * - * It is intended for general purpose use, but was written in support of GRC's - * reference implementation of the SQRL (Secure Quick Reliable Login) client. - * - * See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf - * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \ - * gcm/gcm-revised-spec.pdf - * - * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE - * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. - * - *******************************************************************************/ -#ifndef TLS_AES128_H -#define TLS_AES128_H - -/****************************************************************************** - * AES_CONTEXT : cipher context / holds inter-call data - ******************************************************************************/ -typedef struct { - int mode; // 1 for Encryption, 0 for Decryption - int rounds; // keysize-based rounds count - uint32_t *rk; // pointer to current round key - uint32_t buf[68]; // key expansion buffer -} aes_context; - - -#define GCM_AUTH_FAILURE 0x55555555 // authentication failure - -/****************************************************************************** - * GCM_CONTEXT : MUST be called once before ANY use of this library - ******************************************************************************/ -int mg_gcm_initialize(void); - -// -// aes-gcm.h -// MKo -// -// Created by Markus Kosmal on 20/11/14. -// -// -int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input, - size_t input_length, const unsigned char *key, - const size_t key_len, const unsigned char *iv, - const size_t iv_len, unsigned char *aead, - size_t aead_len, unsigned char *tag, - const size_t tag_len); - -int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input, - size_t input_length, const unsigned char *key, - const size_t key_len, const unsigned char *iv, - const size_t iv_len); - -#endif /* TLS_AES128_H */ - -// End of aes128 PD - - - -#define MG_UECC_SUPPORTS_secp256r1 1 -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_H_ -#define _UECC_H_ - -/* Platform selection options. -If MG_UECC_PLATFORM is not defined, the code will try to guess it based on -compiler macros. Possible values for MG_UECC_PLATFORM are defined below: */ -#define mg_uecc_arch_other 0 -#define mg_uecc_x86 1 -#define mg_uecc_x86_64 2 -#define mg_uecc_arm 3 -#define mg_uecc_arm_thumb 4 -#define mg_uecc_arm_thumb2 5 -#define mg_uecc_arm64 6 -#define mg_uecc_avr 7 - -/* If desired, you can define MG_UECC_WORD_SIZE as appropriate for your platform -(1, 4, or 8 bytes). If MG_UECC_WORD_SIZE is not explicitly defined then it will -be automatically set based on your platform. */ - -/* Optimization level; trade speed for code size. - Larger values produce code that is faster but larger. - Currently supported values are 0 - 4; 0 is unusably slow for most - applications. Optimization level 4 currently only has an effect ARM platforms - where more than one curve is enabled. */ -#ifndef MG_UECC_OPTIMIZATION_LEVEL -#define MG_UECC_OPTIMIZATION_LEVEL 2 -#endif - -/* MG_UECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a -specific function to be used for (scalar) squaring instead of the generic -multiplication function. This can make things faster somewhat faster, but -increases the code size. */ -#ifndef MG_UECC_SQUARE_FUNC -#define MG_UECC_SQUARE_FUNC 0 -#endif - -/* MG_UECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will -switch to native little-endian format for *all* arrays passed in and out of the -public API. This includes public and private keys, shared secrets, signatures -and message hashes. Using this switch reduces the amount of call stack memory -used by uECC, since less intermediate translations are required. Note that this -will *only* work on native little-endian processors and it will treat the -uint8_t arrays passed into the public API as word arrays, therefore requiring -the provided byte arrays to be word aligned on architectures that do not support -unaligned accesses. IMPORTANT: Keys and signatures generated with -MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible with keys and signatures -generated with MG_UECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use the same -endianness. */ -#ifndef MG_UECC_VLI_NATIVE_LITTLE_ENDIAN -#define MG_UECC_VLI_NATIVE_LITTLE_ENDIAN 0 -#endif - -/* Curve support selection. Set to 0 to remove that curve. */ -#ifndef MG_UECC_SUPPORTS_secp160r1 -#define MG_UECC_SUPPORTS_secp160r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp192r1 -#define MG_UECC_SUPPORTS_secp192r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp224r1 -#define MG_UECC_SUPPORTS_secp224r1 0 -#endif -#ifndef MG_UECC_SUPPORTS_secp256r1 -#define MG_UECC_SUPPORTS_secp256r1 1 -#endif -#ifndef MG_UECC_SUPPORTS_secp256k1 -#define MG_UECC_SUPPORTS_secp256k1 0 -#endif - -/* Specifies whether compressed point format is supported. - Set to 0 to disable point compression/decompression functions. */ -#ifndef MG_UECC_SUPPORT_COMPRESSED_POINT -#define MG_UECC_SUPPORT_COMPRESSED_POINT 1 -#endif - -struct MG_UECC_Curve_t; -typedef const struct MG_UECC_Curve_t *MG_UECC_Curve; - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_SUPPORTS_secp160r1 -MG_UECC_Curve mg_uecc_secp160r1(void); -#endif -#if MG_UECC_SUPPORTS_secp192r1 -MG_UECC_Curve mg_uecc_secp192r1(void); -#endif -#if MG_UECC_SUPPORTS_secp224r1 -MG_UECC_Curve mg_uecc_secp224r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256r1 -MG_UECC_Curve mg_uecc_secp256r1(void); -#endif -#if MG_UECC_SUPPORTS_secp256k1 -MG_UECC_Curve mg_uecc_secp256k1(void); -#endif - -/* MG_UECC_RNG_Function type -The RNG function should fill 'size' random bytes into 'dest'. It should return 1 -if 'dest' was filled with random data, or 0 if the random data could not be -generated. The filled-in values should be either truly random, or from a -cryptographically-secure PRNG. - -A correctly functioning RNG function must be set (using mg_uecc_set_rng()) -before calling mg_uecc_make_key() or mg_uecc_sign(). - -Setting a correctly functioning RNG function improves the resistance to -side-channel attacks for mg_uecc_shared_secret() and -mg_uecc_sign_deterministic(). - -A correct RNG function is set by default when building for Windows, Linux, or OS -X. If you are building on another POSIX-compliant system that supports -/dev/random or /dev/urandom, you can define MG_UECC_POSIX to use the predefined -RNG. For embedded platforms there is no predefined RNG function; you must -provide your own. -*/ -typedef int (*MG_UECC_RNG_Function)(uint8_t *dest, unsigned size); - -/* mg_uecc_set_rng() function. -Set the function that will be used to generate random bytes. The RNG function -should return 1 if the random data was generated, or 0 if the random data could -not be generated. - -On platforms where there is no predefined RNG function (eg embedded platforms), -this must be called before mg_uecc_make_key() or mg_uecc_sign() are used. - -Inputs: - rng_function - The function that will be used to generate random bytes. -*/ -void mg_uecc_set_rng(MG_UECC_RNG_Function rng_function); - -/* mg_uecc_get_rng() function. - -Returns the function that will be used to generate random bytes. -*/ -MG_UECC_RNG_Function mg_uecc_get_rng(void); - -/* mg_uecc_curve_private_key_size() function. - -Returns the size of a private key for the curve in bytes. -*/ -int mg_uecc_curve_private_key_size(MG_UECC_Curve curve); - -/* mg_uecc_curve_public_key_size() function. - -Returns the size of a public key for the curve in bytes. -*/ -int mg_uecc_curve_public_key_size(MG_UECC_Curve curve); - -/* mg_uecc_make_key() function. -Create a public/private key pair. - -Outputs: - public_key - Will be filled in with the public key. Must be at least 2 * -the curve size (in bytes) long. For example, if the curve is secp256r1, -public_key must be 64 bytes long. private_key - Will be filled in with the -private key. Must be as long as the curve order; this is typically the same as -the curve size, except for secp160r1. For example, if the curve is secp256r1, -private_key must be 32 bytes long. - - For secp160r1, private_key must be 21 bytes long! Note that -the first byte will almost always be 0 (there is about a 1 in 2^80 chance of it -being non-zero). - -Returns 1 if the key pair was generated successfully, 0 if an error occurred. -*/ -int mg_uecc_make_key(uint8_t *public_key, uint8_t *private_key, - MG_UECC_Curve curve); - -/* mg_uecc_shared_secret() function. -Compute a shared secret given your secret key and someone else's public key. If -the public key is not from a trusted source and has not been previously -verified, you should verify it first using mg_uecc_valid_public_key(). Note: It -is recommended that you hash the result of mg_uecc_shared_secret() before using -it for symmetric encryption or HMAC. - -Inputs: - public_key - The public key of the remote party. - private_key - Your private key. - -Outputs: - secret - Will be filled in with the shared secret value. Must be the same -size as the curve size; for example, if the curve is secp256r1, secret must be -32 bytes long. - -Returns 1 if the shared secret was generated successfully, 0 if an error -occurred. -*/ -int mg_uecc_shared_secret(const uint8_t *public_key, const uint8_t *private_key, - uint8_t *secret, MG_UECC_Curve curve); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* mg_uecc_compress() function. -Compress a public key. - -Inputs: - public_key - The public key to compress. - -Outputs: - compressed - Will be filled in with the compressed public key. Must be at -least (curve size + 1) bytes long; for example, if the curve is secp256r1, - compressed must be 33 bytes long. -*/ -void mg_uecc_compress(const uint8_t *public_key, uint8_t *compressed, - MG_UECC_Curve curve); - -/* mg_uecc_decompress() function. -Decompress a compressed public key. - -Inputs: - compressed - The compressed public key. - -Outputs: - public_key - Will be filled in with the decompressed public key. -*/ -void mg_uecc_decompress(const uint8_t *compressed, uint8_t *public_key, - MG_UECC_Curve curve); -#endif /* MG_UECC_SUPPORT_COMPRESSED_POINT */ - -/* mg_uecc_valid_public_key() function. -Check to see if a public key is valid. - -Note that you are not required to check for a valid public key before using any -other uECC functions. However, you may wish to avoid spending CPU time computing -a shared secret or verifying a signature using an invalid public key. - -Inputs: - public_key - The public key to check. - -Returns 1 if the public key is valid, 0 if it is invalid. -*/ -int mg_uecc_valid_public_key(const uint8_t *public_key, MG_UECC_Curve curve); - -/* mg_uecc_compute_public_key() function. -Compute the corresponding public key for a private key. - -Inputs: - private_key - The private key to compute the public key for - -Outputs: - public_key - Will be filled in with the corresponding public key - -Returns 1 if the key was computed successfully, 0 if an error occurred. -*/ -int mg_uecc_compute_public_key(const uint8_t *private_key, uint8_t *public_key, - MG_UECC_Curve curve); - -/* mg_uecc_sign() function. -Generate an ECDSA signature for a given hash value. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it in to this function along with your private key. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - -Outputs: - signature - Will be filled in with the signature value. Must be at least 2 * -curve size long. For example, if the curve is secp256r1, signature must be 64 -bytes long. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign(const uint8_t *private_key, const uint8_t *message_hash, - unsigned hash_size, uint8_t *signature, MG_UECC_Curve curve); - -/* MG_UECC_HashContext structure. -This is used to pass in an arbitrary hash function to -mg_uecc_sign_deterministic(). The structure will be used for multiple hash -computations; each time a new hash is computed, init_hash() will be called, -followed by one or more calls to update_hash(), and finally a call to -finish_hash() to produce the resulting hash. - -The intention is that you will create a structure that includes -MG_UECC_HashContext followed by any hash-specific data. For example: - -typedef struct SHA256_HashContext { - MG_UECC_HashContext uECC; - SHA256_CTX ctx; -} SHA256_HashContext; - -void init_SHA256(MG_UECC_HashContext *base) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Init(&context->ctx); -} - -void update_SHA256(MG_UECC_HashContext *base, - const uint8_t *message, - unsigned message_size) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Update(&context->ctx, message, message_size); -} - -void finish_SHA256(MG_UECC_HashContext *base, uint8_t *hash_result) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Final(hash_result, &context->ctx); -} - -... when signing ... -{ - uint8_t tmp[32 + 32 + 64]; - SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, -32, tmp}}; mg_uecc_sign_deterministic(key, message_hash, &ctx.uECC, signature); -} -*/ -typedef struct MG_UECC_HashContext { - void (*init_hash)(const struct MG_UECC_HashContext *context); - void (*update_hash)(const struct MG_UECC_HashContext *context, - const uint8_t *message, unsigned message_size); - void (*finish_hash)(const struct MG_UECC_HashContext *context, - uint8_t *hash_result); - unsigned - block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned - result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + - block_size) bytes. */ -} MG_UECC_HashContext; - -/* mg_uecc_sign_deterministic() function. -Generate an ECDSA signature for a given hash value, using a deterministic -algorithm (see RFC 6979). You do not need to set the RNG using mg_uecc_set_rng() -before calling this function; however, if the RNG is defined it will improve -resistance to side-channel attacks. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and -pass it to this function along with your private key and a hash context. Note -that the message_hash does not need to be computed with the same hash function -used by hash_context. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - hash_context - A hash context to use. - -Outputs: - signature - Will be filled in with the signature value. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int mg_uecc_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, unsigned hash_size, - const MG_UECC_HashContext *hash_context, - uint8_t *signature, MG_UECC_Curve curve); - -/* mg_uecc_verify() function. -Verify an ECDSA signature. - -Usage: Compute the hash of the signed data using the same hash as the signer and -pass it to this function along with the signer's public key and the signature -values (r and s). - -Inputs: - public_key - The signer's public key. - message_hash - The hash of the signed data. - hash_size - The size of message_hash in bytes. - signature - The signature value. - -Returns 1 if the signature is valid, 0 if it is invalid. -*/ -int mg_uecc_verify(const uint8_t *public_key, const uint8_t *message_hash, - unsigned hash_size, const uint8_t *signature, - MG_UECC_Curve curve); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_TYPES_H_ -#define _UECC_TYPES_H_ - -#ifndef MG_UECC_PLATFORM -#if defined(__AVR__) && __AVR__ -#define MG_UECC_PLATFORM mg_uecc_avr -#elif defined(__thumb2__) || \ - defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ -#define MG_UECC_PLATFORM mg_uecc_arm_thumb2 -#elif defined(__thumb__) -#define MG_UECC_PLATFORM mg_uecc_arm_thumb -#elif defined(__arm__) || defined(_M_ARM) -#define MG_UECC_PLATFORM mg_uecc_arm -#elif defined(__aarch64__) -#define MG_UECC_PLATFORM mg_uecc_arm64 -#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || \ - defined(__I86__) -#define MG_UECC_PLATFORM mg_uecc_x86 -#elif defined(__amd64__) || defined(_M_X64) -#define MG_UECC_PLATFORM mg_uecc_x86_64 -#else -#define MG_UECC_PLATFORM mg_uecc_arch_other -#endif -#endif - -#ifndef MG_UECC_ARM_USE_UMAAL -#if (MG_UECC_PLATFORM == mg_uecc_arm) && (__ARM_ARCH >= 6) -#define MG_UECC_ARM_USE_UMAAL 1 -#elif (MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && (__ARM_ARCH >= 6) && \ - (!defined(__ARM_ARCH_7M__) || !__ARM_ARCH_7M__) -#define MG_UECC_ARM_USE_UMAAL 1 -#else -#define MG_UECC_ARM_USE_UMAAL 0 -#endif -#endif - -#ifndef MG_UECC_WORD_SIZE -#if MG_UECC_PLATFORM == mg_uecc_avr -#define MG_UECC_WORD_SIZE 1 -#elif (MG_UECC_PLATFORM == mg_uecc_x86_64 || MG_UECC_PLATFORM == mg_uecc_arm64) -#define MG_UECC_WORD_SIZE 8 -#else -#define MG_UECC_WORD_SIZE 4 -#endif -#endif - -#if (MG_UECC_WORD_SIZE != 1) && (MG_UECC_WORD_SIZE != 4) && \ - (MG_UECC_WORD_SIZE != 8) -#error "Unsupported value for MG_UECC_WORD_SIZE" -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_avr) && (MG_UECC_WORD_SIZE != 1)) -#pragma message("MG_UECC_WORD_SIZE must be 1 for AVR") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 1 -#endif - -#if ((MG_UECC_PLATFORM == mg_uecc_arm || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb || \ - MG_UECC_PLATFORM == mg_uecc_arm_thumb2) && \ - (MG_UECC_WORD_SIZE != 4)) -#pragma message("MG_UECC_WORD_SIZE must be 4 for ARM") -#undef MG_UECC_WORD_SIZE -#define MG_UECC_WORD_SIZE 4 -#endif - -typedef int8_t wordcount_t; -typedef int16_t bitcount_t; -typedef int8_t cmpresult_t; - -#if (MG_UECC_WORD_SIZE == 1) - -typedef uint8_t mg_uecc_word_t; -typedef uint16_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80 -#define MG_UECC_WORD_BITS 8 -#define MG_UECC_WORD_BITS_SHIFT 3 -#define MG_UECC_WORD_BITS_MASK 0x07 - -#elif (MG_UECC_WORD_SIZE == 4) - -typedef uint32_t mg_uecc_word_t; -typedef uint64_t mg_uecc_dword_t; - -#define HIGH_BIT_SET 0x80000000 -#define MG_UECC_WORD_BITS 32 -#define MG_UECC_WORD_BITS_SHIFT 5 -#define MG_UECC_WORD_BITS_MASK 0x01F - -#elif (MG_UECC_WORD_SIZE == 8) - -typedef uint64_t mg_uecc_word_t; - -#define HIGH_BIT_SET 0x8000000000000000U -#define MG_UECC_WORD_BITS 64 -#define MG_UECC_WORD_BITS_SHIFT 6 -#define MG_UECC_WORD_BITS_MASK 0x03F - -#endif /* MG_UECC_WORD_SIZE */ - -#endif /* _UECC_TYPES_H_ */ - -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_VLI_H_ -#define _UECC_VLI_H_ - -// -// - -/* Functions for raw large-integer manipulation. These are only available - if uECC.c is compiled with MG_UECC_ENABLE_VLI_API defined to 1. */ -#ifndef MG_UECC_ENABLE_VLI_API -#define MG_UECC_ENABLE_VLI_API 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if MG_UECC_ENABLE_VLI_API - -void mg_uecc_vli_clear(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -mg_uecc_word_t mg_uecc_vli_isZero(const mg_uecc_word_t *vli, - wordcount_t num_words); - -/* Returns nonzero if bit 'bit' of vli is set. */ -mg_uecc_word_t mg_uecc_vli_testBit(const mg_uecc_word_t *vli, bitcount_t bit); - -/* Counts the number of bits required to represent vli. */ -bitcount_t mg_uecc_vli_numBits(const mg_uecc_word_t *vli, - const wordcount_t max_words); - -/* Sets dest = src. */ -void mg_uecc_vli_set(mg_uecc_word_t *dest, const mg_uecc_word_t *src, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise */ -mg_uecc_word_t mg_uecc_vli_equal(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns sign of left - right, in constant time. */ -cmpresult_t mg_uecc_vli_cmp(const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes vli = vli >> 1. */ -void mg_uecc_vli_rshift1(mg_uecc_word_t *vli, wordcount_t num_words); - -/* Computes result = left + right, returning carry. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_add(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left - right, returning borrow. Can modify in place. */ -mg_uecc_word_t mg_uecc_vli_sub(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, - wordcount_t num_words); - -/* Computes result = left * right. Result must be 2 * num_words long. */ -void mg_uecc_vli_mult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, wordcount_t num_words); - -/* Computes result = left^2. Result must be 2 * num_words long. */ -void mg_uecc_vli_square(mg_uecc_word_t *result, const mg_uecc_word_t *left, - wordcount_t num_words); - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modAdd(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap - mod. */ -void mg_uecc_vli_modSub(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = product % mod, where product is 2N words long. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_mmod(mg_uecc_word_t *result, mg_uecc_word_t *product, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Calculates result = product (mod curve->p), where product is up to - 2 * curve->num_words long. */ -void mg_uecc_vli_mmod_fast(mg_uecc_word_t *result, mg_uecc_word_t *product, - MG_UECC_Curve curve); - -/* Computes result = (left * right) % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modMult(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *right, const mg_uecc_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left * right) % curve->p. */ -void mg_uecc_vli_modMult_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - const mg_uecc_word_t *right, MG_UECC_Curve curve); - -/* Computes result = left^2 % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void mg_uecc_vli_modSquare(mg_uecc_word_t *result, const mg_uecc_word_t *left, - const mg_uecc_word_t *mod, wordcount_t num_words); - -/* Computes result = left^2 % curve->p. */ -void mg_uecc_vli_modSquare_fast(mg_uecc_word_t *result, - const mg_uecc_word_t *left, - MG_UECC_Curve curve); - -/* Computes result = (1 / input) % mod.*/ -void mg_uecc_vli_modInv(mg_uecc_word_t *result, const mg_uecc_word_t *input, - const mg_uecc_word_t *mod, wordcount_t num_words); - -#if MG_UECC_SUPPORT_COMPRESSED_POINT -/* Calculates a = sqrt(a) (mod curve->p) */ -void mg_uecc_vli_mod_sqrt(mg_uecc_word_t *a, MG_UECC_Curve curve); -#endif - -/* Converts an integer in uECC native format to big-endian bytes. */ -void mg_uecc_vli_nativeToBytes(uint8_t *bytes, int num_bytes, - const mg_uecc_word_t *native); -/* Converts big-endian bytes to an integer in uECC native format. */ -void mg_uecc_vli_bytesToNative(mg_uecc_word_t *native, const uint8_t *bytes, - int num_bytes); - -unsigned mg_uecc_curve_num_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_bits(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_words(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bytes(MG_UECC_Curve curve); -unsigned mg_uecc_curve_num_n_bits(MG_UECC_Curve curve); - -const mg_uecc_word_t *mg_uecc_curve_p(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_n(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_G(MG_UECC_Curve curve); -const mg_uecc_word_t *mg_uecc_curve_b(MG_UECC_Curve curve); - -int mg_uecc_valid_point(const mg_uecc_word_t *point, MG_UECC_Curve curve); - -/* Multiplies a point by a scalar. Points are represented by the X coordinate - followed by the Y coordinate in the same array, both coordinates are - curve->num_words long. Note that scalar must be curve->num_n_words long (NOT - curve->num_words). */ -void mg_uecc_point_mult(mg_uecc_word_t *result, const mg_uecc_word_t *point, - const mg_uecc_word_t *scalar, MG_UECC_Curve curve); - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -int mg_uecc_generate_random_int(mg_uecc_word_t *random, - const mg_uecc_word_t *top, - wordcount_t num_words); - -#endif /* MG_UECC_ENABLE_VLI_API */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_VLI_H_ */ - -// End of uecc BSD-2 -// portable8439 v1.0.1 -// Source: https://github.com/DavyLandman/portable8439 -// Licensed under CC0-1.0 -// Contains poly1305-donna e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781 (Public -// Domain) - - - - -#ifndef __PORTABLE_8439_H -#define __PORTABLE_8439_H -#if defined(__cplusplus) -extern "C" { -#endif - -// provide your own decl specificier like -DPORTABLE_8439_DECL=ICACHE_RAM_ATTR -#ifndef PORTABLE_8439_DECL -#define PORTABLE_8439_DECL -#endif - -/* - This library implements RFC 8439 a.k.a. ChaCha20-Poly1305 AEAD - - You can use this library to avoid attackers mutating or reusing your - encrypted messages. This does assume you never reuse a nonce+key pair and, - if possible, carefully pick your associated data. -*/ - -/* Make sure we are either nested in C++ or running in a C99+ compiler -#if !defined(__cplusplus) && !defined(_MSC_VER) && \ - (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) -#error "C99 or newer required" -#endif */ - -// #if CHAR_BIT > 8 -// # error "Systems without native octals not suppoted" -// #endif - -#if defined(_MSC_VER) || defined(__cplusplus) -// add restrict support is possible -#if (defined(_MSC_VER) && _MSC_VER >= 1900) || defined(__clang__) || \ - defined(__GNUC__) -#define restrict __restrict -#else -#define restrict -#endif -#endif - -#define RFC_8439_TAG_SIZE (16) -#define RFC_8439_KEY_SIZE (32) -#define RFC_8439_NONCE_SIZE (12) - -/* - Encrypt/Seal plain text bytes into a cipher text that can only be - decrypted by knowing the key, nonce and associated data. - - input: - - key: RFC_8439_KEY_SIZE bytes that all parties have agreed - upon beforehand - - nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated - for the same key. A counter or a pseudo-random value are fine. - - ad: associated data to include with calculating the tag of the - cipher text. Can be null for empty. - - plain_text: data to be encrypted, pointer + size should not overlap - with cipher_text pointer - - output: - - cipher_text: encrypted plain_text with a tag appended. Make sure to - allocate at least plain_text_size + RFC_8439_TAG_SIZE - - returns: - - size of bytes written to cipher_text, can be -1 if overlapping - pointers are passed for plain_text and cipher_text -*/ -PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt( - uint8_t *restrict cipher_text, const uint8_t key[RFC_8439_KEY_SIZE], - const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad, - size_t ad_size, const uint8_t *restrict plain_text, size_t plain_text_size); - -/* - Decrypt/unseal cipher text given the right key, nonce, and additional data. - - input: - - key: RFC_8439_KEY_SIZE bytes that all parties have agreed - upon beforehand - - nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated for - the same key. A counter or a pseudo-random value are fine. - - ad: associated data to include with calculating the tag of the - cipher text. Can be null for empty. - - cipher_text: encrypted message. - - output: - - plain_text: data to be encrypted, pointer + size should not overlap - with cipher_text pointer, leave at least enough room for - cipher_text_size - RFC_8439_TAG_SIZE - - returns: - - size of bytes written to plain_text, -1 signals either: - - incorrect key/nonce/ad - - corrupted cipher_text - - overlapping pointers are passed for plain_text and cipher_text -*/ -PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt( - uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE], - const uint8_t nonce[RFC_8439_NONCE_SIZE], - const uint8_t *restrict cipher_text, size_t cipher_text_size); -#if defined(__cplusplus) -} -#endif -#endif - - -struct mg_connection; -typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, - void *ev_data); -void mg_call(struct mg_connection *c, int ev, void *ev_data); -void mg_error(struct mg_connection *c, const char *fmt, ...); - -enum { - MG_EV_ERROR, // Error char *error_message - MG_EV_OPEN, // Connection created NULL - MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis - MG_EV_RESOLVE, // Host name is resolved NULL - MG_EV_CONNECT, // Connection established NULL - MG_EV_ACCEPT, // Connection accepted NULL - MG_EV_TLS_HS, // TLS handshake succeeded NULL - MG_EV_READ, // Data received from socket long *bytes_read - MG_EV_WRITE, // Data written to socket long *bytes_written - MG_EV_CLOSE, // Connection closed NULL - MG_EV_HTTP_HDRS, // HTTP headers struct mg_http_message * - MG_EV_HTTP_MSG, // Full HTTP request/response struct mg_http_message * - MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message * - MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message * - MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message * - MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message * - MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message * - MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code - MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis - MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data - MG_EV_USER // Starting ID for user events -}; - - - - - - - - - - -struct mg_dns { - const char *url; // DNS server URL - struct mg_connection *c; // DNS server connection -}; - -struct mg_addr { - uint8_t ip[16]; // Holds IPv4 or IPv6 address, in network byte order - uint16_t port; // TCP or UDP port in network byte order - uint8_t scope_id; // IPv6 scope ID - bool is_ip6; // True when address is IPv6 address -}; - -struct mg_mgr { - struct mg_connection *conns; // List of active connections - struct mg_dns dns4; // DNS for IPv4 - struct mg_dns dns6; // DNS for IPv6 - int dnstimeout; // DNS resolve timeout in milliseconds - bool use_dns6; // Use DNS6 server by default, see #1532 - unsigned long nextid; // Next connection ID - unsigned long timerid; // Next timer ID - void *userdata; // Arbitrary user data pointer - void *tls_ctx; // TLS context shared by all TLS sessions - uint16_t mqtt_id; // MQTT IDs for pub/sub - void *active_dns_requests; // DNS requests in progress - struct mg_timer *timers; // Active timers - int epoll_fd; // Used when MG_EPOLL_ENABLE=1 - struct mg_tcpip_if *ifp; // Builtin TCP/IP stack only. Interface pointer - size_t extraconnsize; // Builtin TCP/IP stack only. Extra space - MG_SOCKET_TYPE pipe; // Socketpair end for mg_wakeup() -#if MG_ENABLE_FREERTOS_TCP - SocketSet_t ss; // NOTE(lsm): referenced from socket struct -#endif -}; - -struct mg_connection { - struct mg_connection *next; // Linkage in struct mg_mgr :: connections - struct mg_mgr *mgr; // Our container - struct mg_addr loc; // Local address - struct mg_addr rem; // Remote address - void *fd; // Connected socket, or LWIP data - unsigned long id; // Auto-incrementing unique connection ID - struct mg_iobuf recv; // Incoming data - struct mg_iobuf send; // Outgoing data - struct mg_iobuf prof; // Profile data enabled by MG_ENABLE_PROFILE - struct mg_iobuf rtls; // TLS only. Incoming encrypted data - mg_event_handler_t fn; // User-specified event handler function - void *fn_data; // User-specified function parameter - mg_event_handler_t pfn; // Protocol-specific handler function - void *pfn_data; // Protocol-specific function parameter - char data[MG_DATA_SIZE]; // Arbitrary connection data - void *tls; // TLS specific data - unsigned is_listening : 1; // Listening connection - unsigned is_client : 1; // Outbound (client) connection - unsigned is_accepted : 1; // Accepted (server) connection - unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress - unsigned is_arplooking : 1; // Non-blocking ARP resolution is in progress - unsigned is_connecting : 1; // Non-blocking connect is in progress - unsigned is_tls : 1; // TLS-enabled connection - unsigned is_tls_hs : 1; // TLS handshake is in progress - unsigned is_udp : 1; // UDP connection - unsigned is_websocket : 1; // WebSocket connection - unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator - unsigned is_hexdumping : 1; // Hexdump in/out traffic - unsigned is_draining : 1; // Send remaining data, then close and free - unsigned is_closing : 1; // Close and free the connection immediately - unsigned is_full : 1; // Stop reads, until cleared - unsigned is_resp : 1; // Response is still being generated - unsigned is_readable : 1; // Connection is ready to read - unsigned is_writable : 1; // Connection is ready to write -}; - -void mg_mgr_poll(struct mg_mgr *, int ms); -void mg_mgr_init(struct mg_mgr *); -void mg_mgr_free(struct mg_mgr *); - -struct mg_connection *mg_listen(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_connect(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, - mg_event_handler_t fn, void *fn_data); -void mg_connect_resolved(struct mg_connection *); -bool mg_send(struct mg_connection *, const void *, size_t); -size_t mg_printf(struct mg_connection *, const char *fmt, ...); -size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap); -bool mg_aton(struct mg_str str, struct mg_addr *addr); - -// These functions are used to integrate with custom network stacks -struct mg_connection *mg_alloc_conn(struct mg_mgr *); -void mg_close_conn(struct mg_connection *c); -bool mg_open_listener(struct mg_connection *c, const char *url); - -// Utility functions -bool mg_wakeup(struct mg_mgr *, unsigned long id, const void *buf, size_t len); -bool mg_wakeup_init(struct mg_mgr *); -struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, - unsigned flags, void (*fn)(void *), void *arg); - - - - - - - - -struct mg_http_header { - struct mg_str name; // Header name - struct mg_str value; // Header value -}; - -struct mg_http_message { - struct mg_str method, uri, query, proto; // Request/response line - struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers - struct mg_str body; // Body - struct mg_str head; // Request + headers - struct mg_str message; // Request + headers + body -}; - -// Parameter for mg_http_serve_dir() -struct mg_http_serve_opts { - const char *root_dir; // Web root directory, must be non-NULL - const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml - const char *extra_headers; // Extra HTTP headers to add in responses - const char *mime_types; // Extra mime types, ext1=type1,ext2=type2,.. - const char *page404; // Path to the 404 page, or NULL by default - struct mg_fs *fs; // Filesystem implementation. Use NULL for POSIX -}; - -// Parameter for mg_http_next_multipart -struct mg_http_part { - struct mg_str name; // Form field name - struct mg_str filename; // Filename for file uploads - struct mg_str body; // Part contents -}; - -int mg_http_parse(const char *s, size_t len, struct mg_http_message *); -int mg_http_get_request_len(const unsigned char *buf, size_t buf_len); -void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...); -void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len); -void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm); -struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data); -void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, - const struct mg_http_serve_opts *); -void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm, - const char *path, const struct mg_http_serve_opts *); -void mg_http_reply(struct mg_connection *, int status_code, const char *headers, - const char *body_fmt, ...); -struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name); -struct mg_str mg_http_var(struct mg_str buf, struct mg_str name); -int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t); -int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form); -size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len); -void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t); -long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size); -void mg_http_bauth(struct mg_connection *, const char *user, const char *pass); -struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v); -size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *); -int mg_http_status(const struct mg_http_message *hm); -void mg_hello(const char *url); - - -void mg_http_serve_ssi(struct mg_connection *c, const char *root, - const char *fullpath); - - -#define MG_TLS_NONE 0 // No TLS support -#define MG_TLS_MBED 1 // mbedTLS -#define MG_TLS_OPENSSL 2 // OpenSSL -#define MG_TLS_WOLFSSL 5 // WolfSSL (based on OpenSSL) -#define MG_TLS_BUILTIN 3 // Built-in -#define MG_TLS_CUSTOM 4 // Custom implementation - -#ifndef MG_TLS -#define MG_TLS MG_TLS_NONE -#endif - - - - - -struct mg_tls_opts { - struct mg_str ca; // PEM or DER - struct mg_str cert; // PEM or DER - struct mg_str key; // PEM or DER - struct mg_str name; // If not empty, enable host name verification - int skip_verification; // Skip certificate and host name verification -}; - -void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *opts); -void mg_tls_free(struct mg_connection *); -long mg_tls_send(struct mg_connection *, const void *buf, size_t len); -long mg_tls_recv(struct mg_connection *, void *buf, size_t len); -size_t mg_tls_pending(struct mg_connection *); -void mg_tls_handshake(struct mg_connection *); - -// Private -void mg_tls_ctx_init(struct mg_mgr *); -void mg_tls_ctx_free(struct mg_mgr *); - -// Low-level IO primives used by TLS layer -enum { MG_IO_ERR = -1, MG_IO_WAIT = -2, MG_IO_RESET = -3 }; -long mg_io_send(struct mg_connection *c, const void *buf, size_t len); -long mg_io_recv(struct mg_connection *c, void *buf, size_t len); - - - - - - - -#if MG_TLS == MG_TLS_MBED -#include -#include -#include -#include - -struct mg_tls_ctx { - int dummy; -#ifdef MBEDTLS_SSL_SESSION_TICKETS - mbedtls_ssl_ticket_context tickets; -#endif -}; - -struct mg_tls { - mbedtls_x509_crt ca; // Parsed CA certificate - mbedtls_x509_crt cert; // Parsed certificate - mbedtls_pk_context pk; // Private key context - mbedtls_ssl_context ssl; // SSL/TLS context - mbedtls_ssl_config conf; // SSL-TLS config -#ifdef MBEDTLS_SSL_SESSION_TICKETS - mbedtls_ssl_ticket_context ticket; // Session tickets context -#endif -}; -#endif - - -#if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL - -#include -#include - -struct mg_tls { - BIO_METHOD *bm; - SSL_CTX *ctx; - SSL *ssl; -}; -#endif - - -#define WEBSOCKET_OP_CONTINUE 0 -#define WEBSOCKET_OP_TEXT 1 -#define WEBSOCKET_OP_BINARY 2 -#define WEBSOCKET_OP_CLOSE 8 -#define WEBSOCKET_OP_PING 9 -#define WEBSOCKET_OP_PONG 10 - - - -struct mg_ws_message { - struct mg_str data; // Websocket message data - uint8_t flags; // Websocket message flags -}; - -struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url, - mg_event_handler_t fn, void *fn_data, - const char *fmt, ...); -void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *, - const char *fmt, ...); -size_t mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op); -size_t mg_ws_wrap(struct mg_connection *, size_t len, int op); -size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...); -size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt, - va_list *); - - - - -struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data); -void mg_sntp_request(struct mg_connection *c); -int64_t mg_sntp_parse(const unsigned char *buf, size_t len); - -uint64_t mg_now(void); // Return milliseconds since Epoch - - - - - -#define MQTT_CMD_CONNECT 1 -#define MQTT_CMD_CONNACK 2 -#define MQTT_CMD_PUBLISH 3 -#define MQTT_CMD_PUBACK 4 -#define MQTT_CMD_PUBREC 5 -#define MQTT_CMD_PUBREL 6 -#define MQTT_CMD_PUBCOMP 7 -#define MQTT_CMD_SUBSCRIBE 8 -#define MQTT_CMD_SUBACK 9 -#define MQTT_CMD_UNSUBSCRIBE 10 -#define MQTT_CMD_UNSUBACK 11 -#define MQTT_CMD_PINGREQ 12 -#define MQTT_CMD_PINGRESP 13 -#define MQTT_CMD_DISCONNECT 14 -#define MQTT_CMD_AUTH 15 - -#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01 -#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02 -#define MQTT_PROP_CONTENT_TYPE 0x03 -#define MQTT_PROP_RESPONSE_TOPIC 0x08 -#define MQTT_PROP_CORRELATION_DATA 0x09 -#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER 0x0B -#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11 -#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12 -#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13 -#define MQTT_PROP_AUTHENTICATION_METHOD 0x15 -#define MQTT_PROP_AUTHENTICATION_DATA 0x16 -#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17 -#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18 -#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19 -#define MQTT_PROP_RESPONSE_INFORMATION 0x1A -#define MQTT_PROP_SERVER_REFERENCE 0x1C -#define MQTT_PROP_REASON_STRING 0x1F -#define MQTT_PROP_RECEIVE_MAXIMUM 0x21 -#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22 -#define MQTT_PROP_TOPIC_ALIAS 0x23 -#define MQTT_PROP_MAXIMUM_QOS 0x24 -#define MQTT_PROP_RETAIN_AVAILABLE 0x25 -#define MQTT_PROP_USER_PROPERTY 0x26 -#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27 -#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28 -#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29 -#define MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A - -enum { - MQTT_PROP_TYPE_BYTE, - MQTT_PROP_TYPE_STRING, - MQTT_PROP_TYPE_STRING_PAIR, - MQTT_PROP_TYPE_BINARY_DATA, - MQTT_PROP_TYPE_VARIABLE_INT, - MQTT_PROP_TYPE_INT, - MQTT_PROP_TYPE_SHORT -}; - -enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED }; - -struct mg_mqtt_prop { - uint8_t id; // Enumerated at MQTT5 Reference - uint32_t iv; // Integer value for 8-, 16-, 32-bit integers types - struct mg_str key; // Non-NULL only for user property type - struct mg_str val; // Non-NULL only for UTF-8 types and user properties -}; - -struct mg_mqtt_opts { - struct mg_str user; // Username, can be empty - struct mg_str pass; // Password, can be empty - struct mg_str client_id; // Client ID - struct mg_str topic; // message/subscription topic - struct mg_str message; // message content - uint8_t qos; // message quality of service - uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4 - uint16_t keepalive; // Keep-alive timer in seconds - uint16_t retransmit_id; // For PUBLISH, init to 0 - bool retain; // Retain flag - bool clean; // Clean session flag - struct mg_mqtt_prop *props; // MQTT5 props array - size_t num_props; // number of props - struct mg_mqtt_prop *will_props; // Valid only for CONNECT packet (MQTT5) - size_t num_will_props; // Number of will props -}; - -struct mg_mqtt_message { - struct mg_str topic; // Parsed topic for PUBLISH - struct mg_str data; // Parsed message for PUBLISH - struct mg_str dgram; // Whole MQTT packet, including headers - uint16_t id; // For PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH - uint8_t cmd; // MQTT command, one of MQTT_CMD_* - uint8_t qos; // Quality of service - uint8_t ack; // CONNACK return code, 0 = success - size_t props_start; // Offset to the start of the properties (MQTT5) - size_t props_size; // Length of the properties -}; - -struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url, - const struct mg_mqtt_opts *opts, - mg_event_handler_t fn, void *fn_data); -struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data); -void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts); -uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts); -void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts); -int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *); -void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, - uint32_t len); -void mg_mqtt_ping(struct mg_connection *); -void mg_mqtt_pong(struct mg_connection *); -void mg_mqtt_disconnect(struct mg_connection *, const struct mg_mqtt_opts *); -size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *, - size_t ofs); - - - - - -// Mongoose sends DNS queries that contain only one question: -// either A (IPv4) or AAAA (IPv6) address lookup. -// Therefore, we expect zero or one answer. -// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address. -struct mg_dns_message { - uint16_t txnid; // Transaction ID - bool resolved; // Resolve successful, addr is set - struct mg_addr addr; // Resolved address - char name[256]; // Host name -}; - -struct mg_dns_header { - uint16_t txnid; // Transaction ID - uint16_t flags; - uint16_t num_questions; - uint16_t num_answers; - uint16_t num_authority_prs; - uint16_t num_other_prs; -}; - -// DNS resource record -struct mg_dns_rr { - uint16_t nlen; // Name or pointer length - uint16_t atype; // Address type - uint16_t aclass; // Address class - uint16_t alen; // Address length -}; - -void mg_resolve(struct mg_connection *, const char *url); -void mg_resolve_cancel(struct mg_connection *); -bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *); -size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, - bool is_question, struct mg_dns_rr *); - - - - - -#ifndef MG_JSON_MAX_DEPTH -#define MG_JSON_MAX_DEPTH 30 -#endif - -// Error return values - negative. Successful returns are >= 0 -enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 }; -int mg_json_get(struct mg_str json, const char *path, int *toklen); - -struct mg_str mg_json_get_tok(struct mg_str json, const char *path); -bool mg_json_get_num(struct mg_str json, const char *path, double *v); -bool mg_json_get_bool(struct mg_str json, const char *path, bool *v); -long mg_json_get_long(struct mg_str json, const char *path, long dflt); -char *mg_json_get_str(struct mg_str json, const char *path); -char *mg_json_get_hex(struct mg_str json, const char *path, int *len); -char *mg_json_get_b64(struct mg_str json, const char *path, int *len); - -bool mg_json_unescape(struct mg_str str, char *buf, size_t len); -size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key, - struct mg_str *val); - - - - -// JSON-RPC request descriptor -struct mg_rpc_req { - struct mg_rpc **head; // RPC handlers list head - struct mg_rpc *rpc; // RPC handler being called - mg_pfn_t pfn; // Response printing function - void *pfn_data; // Response printing function data - void *req_data; // Arbitrary request data - struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]} -}; - -// JSON-RPC method handler -struct mg_rpc { - struct mg_rpc *next; // Next in list - struct mg_str method; // Method pattern - void (*fn)(struct mg_rpc_req *); // Handler function - void *fn_data; // Handler function argument -}; - -void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern, - void (*handler)(struct mg_rpc_req *), void *handler_data); -void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *)); -void mg_rpc_process(struct mg_rpc_req *); - -// Helper functions to print result or error frame -void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...); -void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap); -void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...); -void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *); -void mg_rpc_list(struct mg_rpc_req *r); -// Copyright (c) 2023 Cesanta Software Limited -// All rights reserved - - - - - -#define MG_OTA_NONE 0 // No OTA support -#define MG_OTA_STM32H5 1 // STM32 H5 -#define MG_OTA_STM32H7 2 // STM32 H7 -#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core -#define MG_OTA_STM32F 4 // STM32 F7/F4/F2 -#define MG_OTA_CH32V307 100 // WCH CH32V307 -#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6 -#define MG_OTA_RT1020 300 // IMXRT1020 -#define MG_OTA_RT1060 301 // IMXRT1060 -#define MG_OTA_RT1064 302 // IMXRT1064 -#define MG_OTA_RT1170 303 // IMXRT1170 -#define MG_OTA_MCXN 310 // MCXN947 -#define MG_OTA_FLASH 900 // OTA via an internal flash -#define MG_OTA_ESP32 910 // ESP32 OTA implementation -#define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash -#define MG_OTA_CUSTOM 1000 // Custom implementation - -#ifndef MG_OTA -#define MG_OTA MG_OTA_NONE -#else -#ifndef MG_IRAM -#if defined(__GNUC__) -#define MG_IRAM __attribute__((noinline, section(".iram"))) -#else -#define MG_IRAM -#endif // compiler -#endif // IRAM -#endif // OTA - -// Firmware update API -bool mg_ota_begin(size_t new_firmware_size); // Start writing -bool mg_ota_write(const void *buf, size_t len); // Write chunk, aligned to 1k -bool mg_ota_end(void); // Stop writing - - - -#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM - -struct mg_flash { - void *start; // Address at which flash starts - size_t size; // Flash size - size_t secsz; // Sector size - size_t align; // Write alignment - bool (*write_fn)(void *, const void *, size_t); // Write function - bool (*swap_fn)(void); // Swap partitions -}; - -bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash); -bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash); -bool mg_ota_flash_end(struct mg_flash *flash); - -#endif - - - - - - - -struct mg_tcpip_if; // Mongoose TCP/IP network interface - -struct mg_tcpip_driver { - bool (*init)(struct mg_tcpip_if *); // Init driver - size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit frame - size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Receive frame - bool (*up)(struct mg_tcpip_if *); // Up/down status -}; - -typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev, - void *ev_data); - -enum { - MG_TCPIP_EV_ST_CHG, // state change uint8_t * (&ifp->state) - MG_TCPIP_EV_DHCP_DNS, // DHCP DNS assignment uint32_t *ipaddr - MG_TCPIP_EV_DHCP_SNTP, // DHCP SNTP assignment uint32_t *ipaddr - MG_TCPIP_EV_ARP, // Got ARP packet struct mg_str * - MG_TCPIP_EV_TIMER_1S, // 1 second timer NULL - MG_TCPIP_EV_USER // Starting ID for user events -}; - -// Network interface -struct mg_tcpip_if { - uint8_t mac[6]; // MAC address. Must be set to a valid MAC - uint32_t ip, mask, gw; // IP address, mask, default gateway - struct mg_str tx; // Output (TX) buffer - bool enable_dhcp_client; // Enable DCHP client - bool enable_dhcp_server; // Enable DCHP server - bool enable_get_gateway; // DCHP server sets client as gateway - bool enable_req_dns; // DCHP client requests DNS server - bool enable_req_sntp; // DCHP client requests SNTP server - bool enable_crc32_check; // Do a CRC check on RX frames and strip it - bool enable_mac_check; // Do a MAC check on RX frames - struct mg_tcpip_driver *driver; // Low level driver - void *driver_data; // Driver-specific data - mg_tcpip_event_handler_t fn; // User-specified event handler function - struct mg_mgr *mgr; // Mongoose event manager - struct mg_queue recv_queue; // Receive queue - uint16_t mtu; // Interface MTU -#define MG_TCPIP_MTU_DEFAULT 1500 - - // Internal state, user can use it but should not change it - uint8_t gwmac[6]; // Router's MAC - uint64_t now; // Current time - uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state - uint64_t lease_expire; // Lease expiration time, in ms - uint16_t eport; // Next ephemeral port - volatile uint32_t ndrop; // Number of received, but dropped frames - volatile uint32_t nrecv; // Number of received frames - volatile uint32_t nsent; // Number of transmitted frames - volatile uint32_t nerr; // Number of driver errors - uint8_t state; // Current state -#define MG_TCPIP_STATE_DOWN 0 // Interface is down -#define MG_TCPIP_STATE_UP 1 // Interface is up -#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state -#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned -#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work -}; - -void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *); -void mg_tcpip_free(struct mg_tcpip_if *); -void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp); -void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac); - -extern struct mg_tcpip_driver mg_tcpip_driver_stm32f; -extern struct mg_tcpip_driver mg_tcpip_driver_w5500; -extern struct mg_tcpip_driver mg_tcpip_driver_tm4c; -extern struct mg_tcpip_driver mg_tcpip_driver_tms570; -extern struct mg_tcpip_driver mg_tcpip_driver_stm32h; -extern struct mg_tcpip_driver mg_tcpip_driver_imxrt; -extern struct mg_tcpip_driver mg_tcpip_driver_same54; -extern struct mg_tcpip_driver mg_tcpip_driver_cmsis; -extern struct mg_tcpip_driver mg_tcpip_driver_ra; -extern struct mg_tcpip_driver mg_tcpip_driver_xmc; -extern struct mg_tcpip_driver mg_tcpip_driver_xmc7; -extern struct mg_tcpip_driver mg_tcpip_driver_ppp; -extern struct mg_tcpip_driver mg_tcpip_driver_pico_w; - -// Drivers that require SPI, can use this SPI abstraction -struct mg_tcpip_spi { - void *spi; // Opaque SPI bus descriptor - void (*begin)(void *); // SPI begin: slave select low - void (*end)(void *); // SPI end: slave select high - uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply -}; - - - -// Macros to record timestamped events that happens with a connection. -// They are saved into a c->prof IO buffer, each event is a name and a 32-bit -// timestamp in milliseconds since connection init time. -// -// Test (run in two separate terminals): -// make -C examples/http-server/ CFLAGS_EXTRA=-DMG_ENABLE_PROFILE=1 -// curl localhost:8000 -// Output: -// 1ea1f1e7 2 net.c:150:mg_close_conn 3 profile: -// 1ea1f1e8 2 net.c:150:mg_close_conn 1ea1f1e6 init -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_OPEN -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_ACCEPT -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_READ -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_HTTP_MSG -// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_WRITE -// 1ea1f1e8 2 net.c:150:mg_close_conn 1 EV_CLOSE -// -// Usage: -// Enable profiling by setting MG_ENABLE_PROFILE=1 -// Invoke MG_PROF_ADD(c, "MY_EVENT_1") in the places you'd like to measure - -#if MG_ENABLE_PROFILE -struct mg_profitem { - const char *name; // Event name - uint32_t timestamp; // Milliseconds since connection creation (MG_EV_OPEN) -}; - -#define MG_PROFILE_ALLOC_GRANULARITY 256 // Can save 32 items wih to realloc - -// Adding a profile item to the c->prof. Must be as fast as possible. -// Reallocation of the c->prof iobuf is not desirable here, that's why we -// pre-allocate c->prof with MG_PROFILE_ALLOC_GRANULARITY. -// This macro just inits and copies 8 bytes, and calls mg_millis(), -// which should be fast enough. -#define MG_PROF_ADD(c, name_) \ - do { \ - struct mg_iobuf *io = &c->prof; \ - uint32_t inittime = ((struct mg_profitem *) io->buf)->timestamp; \ - struct mg_profitem item = {name_, (uint32_t) mg_millis() - inittime}; \ - mg_iobuf_add(io, io->len, &item, sizeof(item)); \ - } while (0) - -// Initialising profile for a new connection. Not time sensitive -#define MG_PROF_INIT(c) \ - do { \ - struct mg_profitem first = {"init", (uint32_t) mg_millis()}; \ - mg_iobuf_init(&(c)->prof, 0, MG_PROFILE_ALLOC_GRANULARITY); \ - mg_iobuf_add(&c->prof, c->prof.len, &first, sizeof(first)); \ - } while (0) - -#define MG_PROF_FREE(c) mg_iobuf_free(&(c)->prof) - -// Dumping the profile. Not time sensitive -#define MG_PROF_DUMP(c) \ - do { \ - struct mg_iobuf *io = &c->prof; \ - struct mg_profitem *p = (struct mg_profitem *) io->buf; \ - struct mg_profitem *e = &p[io->len / sizeof(*p)]; \ - MG_INFO(("%lu profile:", c->id)); \ - while (p < e) { \ - MG_INFO(("%5lx %s", (unsigned long) p->timestamp, p->name)); \ - p++; \ - } \ - } while (0) - -#else -#define MG_PROF_INIT(c) -#define MG_PROF_FREE(c) -#define MG_PROF_ADD(c, name) -#define MG_PROF_DUMP(c) -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS - -#include "Driver_ETH_MAC.h" // keep this include -#include "Driver_ETH_PHY.h" // keep this include - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_IMXRT) && MG_ENABLE_DRIVER_IMXRT - -struct mg_tcpip_driver_imxrt_data { - // MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk), - // must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz - // 37.5.1.8.2, Table 37-46 : f = ipg_clk / (2(mdc_cr + 1)) - // ipg_clk mdc_cr VALUE - // -------------------------- - // -1 <-- TODO() tell driver to guess the value - // 25 MHz 4 - // 33 MHz 6 - // 40 MHz 7 - // 50 MHz 9 - // 66 MHz 13 - int mdc_cr; // Valid values: -1 to 63 - - uint8_t phy_addr; // PHY address -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 2 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 24 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_imxrt_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_imxrt; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: imxrt, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - - - -struct mg_phy { - uint16_t (*read_reg)(uint8_t addr, uint8_t reg); - void (*write_reg)(uint8_t addr, uint8_t reg, uint16_t value); -}; - -// PHY configuration settings, bitmask -enum { - // Set if PHY LEDs are connected to ground - MG_PHY_LEDS_ACTIVE_HIGH = (1 << 0), - // Set when PHY clocks MAC. Otherwise, MAC clocks PHY - MG_PHY_CLOCKS_MAC = (1 << 1), -}; - -enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M }; - -void mg_phy_init(struct mg_phy *, uint8_t addr, uint8_t config); -bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex, - uint8_t *speed); - - -#if MG_ENABLE_TCPIP && MG_ARCH == MG_ARCH_PICOSDK && \ - defined(MG_ENABLE_DRIVER_PICO_W) && MG_ENABLE_DRIVER_PICO_W - -#include "cyw43.h" // keep this include -#include "pico/cyw43_arch.h" // keep this include -#include "pico/unique_id.h" // keep this include - -struct mg_tcpip_driver_pico_w_data { - char *ssid; - char *pass; -}; - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_pico_w_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - MG_SET_WIFI_CREDS(&driver_data_.ssid, &driver_data_.pass); \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_pico_w; \ - mif_.driver_data = &driver_data_; \ - mif_.recv_queue.size = 8192; \ - mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: pico-w, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -struct mg_tcpip_driver_ppp_data { - void *uart; // Opaque UART bus descriptor - void (*reset)(void *); // Modem hardware reset - void (*tx)(void *, uint8_t); // UART transmit single byte - int (*rx)(void *); // UART receive single byte - const char **script; // List of AT commands and expected replies - int script_index; // Index of the current AT command in the list - uint64_t deadline; // AT command deadline in ms -}; - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RA) && MG_ENABLE_DRIVER_RA - -struct mg_tcpip_driver_ra_data { - // MDC clock "divider". MDC clock is software generated, - uint32_t clock; // core clock frequency in Hz - uint16_t irqno; // IRQn, R_ICU->IELSR[irqno] - uint8_t phy_addr; // PHY address -}; - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && MG_ENABLE_DRIVER_SAME54 - -struct mg_tcpip_driver_same54_data { - int mdc_cr; -}; - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 5 -#endif - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \ - MG_ENABLE_DRIVER_STM32F - -struct mg_tcpip_driver_stm32f_data { - // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz - // HCLK range DIVIDER mdc_cr VALUE - // ------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz HCLK/42 0 - // 100-150 MHz HCLK/62 1 - // 20-35 MHz HCLK/16 2 - // 35-60 MHz HCLK/26 3 - // 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed - // 216-310 MHz HCLK/124 5 - // 110, 111 Reserved - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - - uint8_t phy_addr; // PHY address -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 4 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_stm32f_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_stm32f; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: stm32f, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP -#if !defined(MG_ENABLE_DRIVER_STM32H) -#define MG_ENABLE_DRIVER_STM32H 0 -#endif -#if !defined(MG_ENABLE_DRIVER_MCXN) -#define MG_ENABLE_DRIVER_MCXN 0 -#endif -#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN - -struct mg_tcpip_driver_stm32h_data { - // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz - // HCLK range DIVIDER mdc_cr VALUE - // ------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz HCLK/42 0 - // 100-150 MHz HCLK/62 1 - // 20-35 MHz HCLK/16 2 - // 35-60 MHz HCLK/26 3 - // 150-250 MHz HCLK/102 4 <-- value for max speed HSI - // 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI - // 300-500 MHz HCLK/204 6 - // 500-800 MHz HCLK/324 7 - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - - uint8_t phy_addr; // PHY address - uint8_t phy_conf; // PHY config -}; - -#ifndef MG_TCPIP_PHY_CONF -#define MG_TCPIP_PHY_CONF MG_PHY_CLOCKS_MAC -#endif - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 4 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_stm32h_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - driver_data_.phy_conf = MG_TCPIP_PHY_CONF; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_stm32h; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C - -struct mg_tcpip_driver_tm4c_data { - // MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz - // SYSCLK range DIVIDER mdc_cr VALUE - // ------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz SYSCLK/42 0 - // 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed - // 20-35 MHz SYSCLK/16 2 - // 35-60 MHz SYSCLK/26 3 - // 0x4-0xF Reserved - int mdc_cr; // Valid values: -1, 0, 1, 2, 3 -}; - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 1 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_tm4c_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_tm4c; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: tm4c, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TMS570) && MG_ENABLE_DRIVER_TMS570 -struct mg_tcpip_driver_tms570_data { - int mdc_cr; - int phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 1 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_tms570_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_tms570; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: tms570, MAC: %M", mg_print_mac, mif_.mac));\ - } while (0) -#endif - - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC - -struct mg_tcpip_driver_xmc_data { - // 13.2.8.1 Station Management Functions - // MDC clock divider (). MDC clock is derived from ETH MAC clock - // It must not exceed 2.5MHz - // ETH Clock range DIVIDER mdc_cr VALUE - // -------------------------------------------- - // -1 <-- tell driver to guess the value - // 60-100 MHz ETH Clock/42 0 - // 100-150 MHz ETH Clock/62 1 - // 20-35 MHz ETH Clock/16 2 - // 35-60 MHz ETH Clock/26 3 - // 150-250 MHz ETH Clock/102 4 - // 250-300 MHz ETH Clock/124 5 - // 110, 111 Reserved - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - uint8_t phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 4 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_xmc_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_xmc; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 - -struct mg_tcpip_driver_xmc7_data { - int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5 - uint8_t phy_addr; -}; - -#ifndef MG_TCPIP_PHY_ADDR -#define MG_TCPIP_PHY_ADDR 0 -#endif - -#ifndef MG_DRIVER_MDC_CR -#define MG_DRIVER_MDC_CR 3 -#endif - -#define MG_TCPIP_DRIVER_INIT(mgr) \ - do { \ - static struct mg_tcpip_driver_xmc7_data driver_data_; \ - static struct mg_tcpip_if mif_; \ - driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \ - driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \ - mif_.ip = MG_TCPIP_IP; \ - mif_.mask = MG_TCPIP_MASK; \ - mif_.gw = MG_TCPIP_GW; \ - mif_.driver = &mg_tcpip_driver_xmc7; \ - mif_.driver_data = &driver_data_; \ - MG_SET_MAC_ADDRESS(mif_.mac); \ - mg_tcpip_init(mgr, &mif_); \ - MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \ - } while (0) - -#endif - - -#ifdef __cplusplus -} -#endif -#endif // MONGOOSE_H diff --git a/test/tls_multirec/patched_mongoose.c b/test/tls_multirec/patched_mongoose.c deleted file mode 100644 index aa576077..00000000 --- a/test/tls_multirec/patched_mongoose.c +++ /dev/null @@ -1,20313 +0,0 @@ -// Copyright (c) 2004-2013 Sergey Lyubka -// Copyright (c) 2013-2024 Cesanta Software Limited -// All rights reserved -// -// This software is dual-licensed: you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. For the terms of this -// license, see http://www.gnu.org/licenses/ -// -// You are free to use this software under the terms of the GNU General -// Public License, but WITHOUT ANY WARRANTY; without even the implied -// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -// See the GNU General Public License for more details. -// -// Alternatively, you can license this software under a commercial -// license, as set out in https://www.mongoose.ws/licensing/ -// -// SPDX-License-Identifier: GPL-2.0-only or commercial - -#include "mongoose.h" - -#ifdef MG_ENABLE_LINES -#line 1 "src/base64.c" -#endif - - -static int mg_base64_encode_single(int c) { - if (c < 26) { - return c + 'A'; - } else if (c < 52) { - return c - 26 + 'a'; - } else if (c < 62) { - return c - 52 + '0'; - } else { - return c == 62 ? '+' : '/'; - } -} - -static int mg_base64_decode_single(int c) { - if (c >= 'A' && c <= 'Z') { - return c - 'A'; - } else if (c >= 'a' && c <= 'z') { - return c + 26 - 'a'; - } else if (c >= '0' && c <= '9') { - return c + 52 - '0'; - } else if (c == '+') { - return 62; - } else if (c == '/') { - return 63; - } else if (c == '=') { - return 64; - } else { - return -1; - } -} - -size_t mg_base64_update(unsigned char ch, char *to, size_t n) { - unsigned long rem = (n & 3) % 3; - if (rem == 0) { - to[n] = (char) mg_base64_encode_single(ch >> 2); - to[++n] = (char) ((ch & 3) << 4); - } else if (rem == 1) { - to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 4)); - to[++n] = (char) ((ch & 15) << 2); - } else { - to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 6)); - to[++n] = (char) mg_base64_encode_single(ch & 63); - n++; - } - return n; -} - -size_t mg_base64_final(char *to, size_t n) { - size_t saved = n; - // printf("---[%.*s]\n", n, to); - if (n & 3) n = mg_base64_update(0, to, n); - if ((saved & 3) == 2) n--; - // printf(" %d[%.*s]\n", n, n, to); - while (n & 3) to[n++] = '='; - to[n] = '\0'; - return n; -} - -size_t mg_base64_encode(const unsigned char *p, size_t n, char *to, size_t dl) { - size_t i, len = 0; - if (dl > 0) to[0] = '\0'; - if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) return 0; - for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len); - len = mg_base64_final(to, len); - return len; -} - -size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) { - const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL - size_t len = 0; - if (dl < n / 4 * 3 + 1) goto fail; - while (src != NULL && src + 3 < end) { - int a = mg_base64_decode_single(src[0]), - b = mg_base64_decode_single(src[1]), - c = mg_base64_decode_single(src[2]), - d = mg_base64_decode_single(src[3]); - if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) { - goto fail; - } - dst[len++] = (char) ((a << 2) | (b >> 4)); - if (src[2] != '=') { - dst[len++] = (char) ((b << 4) | (c >> 2)); - if (src[3] != '=') dst[len++] = (char) ((c << 6) | d); - } - src += 4; - } - dst[len] = '\0'; - return len; -fail: - if (dl > 0) dst[0] = '\0'; - return 0; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/dns.c" -#endif - - - - - - - - -struct dns_data { - struct dns_data *next; - struct mg_connection *c; - uint64_t expire; - uint16_t txnid; -}; - -static void mg_sendnsreq(struct mg_connection *, struct mg_str *, int, - struct mg_dns *, bool); - -static void mg_dns_free(struct dns_data **head, struct dns_data *d) { - LIST_DELETE(struct dns_data, head, d); - free(d); -} - -void mg_resolve_cancel(struct mg_connection *c) { - struct dns_data *tmp, *d; - struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests; - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - if (d->c == c) mg_dns_free(head, d); - } -} - -static size_t mg_dns_parse_name_depth(const uint8_t *s, size_t len, size_t ofs, - char *to, size_t tolen, size_t j, - int depth) { - size_t i = 0; - if (tolen > 0 && depth == 0) to[0] = '\0'; - if (depth > 5) return 0; - // MG_INFO(("ofs %lx %x %x", (unsigned long) ofs, s[ofs], s[ofs + 1])); - while (ofs + i + 1 < len) { - size_t n = s[ofs + i]; - if (n == 0) { - i++; - break; - } - if (n & 0xc0) { - size_t ptr = (((n & 0x3f) << 8) | s[ofs + i + 1]); // 12 is hdr len - // MG_INFO(("PTR %lx", (unsigned long) ptr)); - if (ptr + 1 < len && (s[ptr] & 0xc0) == 0 && - mg_dns_parse_name_depth(s, len, ptr, to, tolen, j, depth + 1) == 0) - return 0; - i += 2; - break; - } - if (ofs + i + n + 1 >= len) return 0; - if (j > 0) { - if (j < tolen) to[j] = '.'; - j++; - } - if (j + n < tolen) memcpy(&to[j], &s[ofs + i + 1], n); - j += n; - i += n + 1; - if (j < tolen) to[j] = '\0'; // Zero-terminate this chunk - // MG_INFO(("--> [%s]", to)); - } - if (tolen > 0) to[tolen - 1] = '\0'; // Make sure make sure it is nul-term - return i; -} - -static size_t mg_dns_parse_name(const uint8_t *s, size_t n, size_t ofs, - char *dst, size_t dstlen) { - return mg_dns_parse_name_depth(s, n, ofs, dst, dstlen, 0, 0); -} - -size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, - bool is_question, struct mg_dns_rr *rr) { - const uint8_t *s = buf + ofs, *e = &buf[len]; - - memset(rr, 0, sizeof(*rr)); - if (len < sizeof(struct mg_dns_header)) return 0; // Too small - if (len > 512) return 0; // Too large, we don't expect that - if (s >= e) return 0; // Overflow - - if ((rr->nlen = (uint16_t) mg_dns_parse_name(buf, len, ofs, NULL, 0)) == 0) - return 0; - s += rr->nlen + 4; - if (s > e) return 0; - rr->atype = (uint16_t) (((uint16_t) s[-4] << 8) | s[-3]); - rr->aclass = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]); - if (is_question) return (size_t) (rr->nlen + 4); - - s += 6; - if (s > e) return 0; - rr->alen = (uint16_t) (((uint16_t) s[-2] << 8) | s[-1]); - if (s + rr->alen > e) return 0; - return (size_t) (rr->nlen + rr->alen + 10); -} - -bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) { - const struct mg_dns_header *h = (struct mg_dns_header *) buf; - struct mg_dns_rr rr; - size_t i, n, num_answers, ofs = sizeof(*h); - memset(dm, 0, sizeof(*dm)); - - if (len < sizeof(*h)) return 0; // Too small, headers dont fit - if (mg_ntohs(h->num_questions) > 1) return 0; // Sanity - num_answers = mg_ntohs(h->num_answers); - if (num_answers > 10) { - MG_DEBUG(("Got %u answers, ignoring beyond 10th one", num_answers)); - num_answers = 10; // Sanity cap - } - dm->txnid = mg_ntohs(h->txnid); - - for (i = 0; i < mg_ntohs(h->num_questions); i++) { - if ((n = mg_dns_parse_rr(buf, len, ofs, true, &rr)) == 0) return false; - // MG_INFO(("Q %lu %lu %hu/%hu", ofs, n, rr.atype, rr.aclass)); - ofs += n; - } - for (i = 0; i < num_answers; i++) { - if ((n = mg_dns_parse_rr(buf, len, ofs, false, &rr)) == 0) return false; - // MG_INFO(("A -- %lu %lu %hu/%hu %s", ofs, n, rr.atype, rr.aclass, - // dm->name)); - mg_dns_parse_name(buf, len, ofs, dm->name, sizeof(dm->name)); - ofs += n; - - if (rr.alen == 4 && rr.atype == 1 && rr.aclass == 1) { - dm->addr.is_ip6 = false; - memcpy(&dm->addr.ip, &buf[ofs - 4], 4); - dm->resolved = true; - break; // Return success - } else if (rr.alen == 16 && rr.atype == 28 && rr.aclass == 1) { - dm->addr.is_ip6 = true; - memcpy(&dm->addr.ip, &buf[ofs - 16], 16); - dm->resolved = true; - break; // Return success - } - } - return true; -} - -static void dns_cb(struct mg_connection *c, int ev, void *ev_data) { - struct dns_data *d, *tmp; - struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests; - if (ev == MG_EV_POLL) { - uint64_t now = *(uint64_t *) ev_data; - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - // MG_DEBUG ("%lu %lu dns poll", d->expire, now)); - if (now > d->expire) mg_error(d->c, "DNS timeout"); - } - } else if (ev == MG_EV_READ) { - struct mg_dns_message dm; - int resolved = 0; - if (mg_dns_parse(c->recv.buf, c->recv.len, &dm) == false) { - MG_ERROR(("Unexpected DNS response:")); - mg_hexdump(c->recv.buf, c->recv.len); - } else { - // MG_VERBOSE(("%s %d", dm.name, dm.resolved)); - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - // MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid)); - if (dm.txnid != d->txnid) continue; - if (d->c->is_resolving) { - if (dm.resolved) { - dm.addr.port = d->c->rem.port; // Save port - d->c->rem = dm.addr; // Copy resolved address - MG_DEBUG( - ("%lu %s is %M", d->c->id, dm.name, mg_print_ip, &d->c->rem)); - mg_connect_resolved(d->c); -#if MG_ENABLE_IPV6 - } else if (dm.addr.is_ip6 == false && dm.name[0] != '\0' && - c->mgr->use_dns6 == false) { - struct mg_str x = mg_str(dm.name); - mg_sendnsreq(d->c, &x, c->mgr->dnstimeout, &c->mgr->dns6, true); -#endif - } else { - mg_error(d->c, "%s DNS lookup failed", dm.name); - } - } else { - MG_ERROR(("%lu already resolved", d->c->id)); - } - mg_dns_free(head, d); - resolved = 1; - } - } - if (!resolved) MG_ERROR(("stray DNS reply")); - c->recv.len = 0; - } else if (ev == MG_EV_CLOSE) { - for (d = *head; d != NULL; d = tmp) { - tmp = d->next; - mg_error(d->c, "DNS error"); - mg_dns_free(head, d); - } - } -} - -static bool mg_dns_send(struct mg_connection *c, const struct mg_str *name, - uint16_t txnid, bool ipv6) { - struct { - struct mg_dns_header header; - uint8_t data[256]; - } pkt; - size_t i, n; - memset(&pkt, 0, sizeof(pkt)); - pkt.header.txnid = mg_htons(txnid); - pkt.header.flags = mg_htons(0x100); - pkt.header.num_questions = mg_htons(1); - for (i = n = 0; i < sizeof(pkt.data) - 5; i++) { - if (name->buf[i] == '.' || i >= name->len) { - pkt.data[n] = (uint8_t) (i - n); - memcpy(&pkt.data[n + 1], name->buf + n, i - n); - n = i + 1; - } - if (i >= name->len) break; - } - memcpy(&pkt.data[n], "\x00\x00\x01\x00\x01", 5); // A query - n += 5; - if (ipv6) pkt.data[n - 3] = 0x1c; // AAAA query - // memcpy(&pkt.data[n], "\xc0\x0c\x00\x1c\x00\x01", 6); // AAAA query - // n += 6; - return mg_send(c, &pkt, sizeof(pkt.header) + n); -} - -static void mg_sendnsreq(struct mg_connection *c, struct mg_str *name, int ms, - struct mg_dns *dnsc, bool ipv6) { - struct dns_data *d = NULL; - if (dnsc->url == NULL) { - mg_error(c, "DNS server URL is NULL. Call mg_mgr_init()"); - } else if (dnsc->c == NULL) { - dnsc->c = mg_connect(c->mgr, dnsc->url, NULL, NULL); - if (dnsc->c != NULL) { - dnsc->c->pfn = dns_cb; - // dnsc->c->is_hexdumping = 1; - } - } - if (dnsc->c == NULL) { - mg_error(c, "resolver"); - } else if ((d = (struct dns_data *) calloc(1, sizeof(*d))) == NULL) { - mg_error(c, "resolve OOM"); - } else { - struct dns_data *reqs = (struct dns_data *) c->mgr->active_dns_requests; - d->txnid = reqs ? (uint16_t) (reqs->txnid + 1) : 1; - d->next = (struct dns_data *) c->mgr->active_dns_requests; - c->mgr->active_dns_requests = d; - d->expire = mg_millis() + (uint64_t) ms; - d->c = c; - c->is_resolving = 1; - MG_VERBOSE(("%lu resolving %.*s @ %s, txnid %hu", c->id, (int) name->len, - name->buf, dnsc->url, d->txnid)); - if (!mg_dns_send(dnsc->c, name, d->txnid, ipv6)) { - mg_error(dnsc->c, "DNS send"); - } - } -} - -void mg_resolve(struct mg_connection *c, const char *url) { - struct mg_str host = mg_url_host(url); - c->rem.port = mg_htons(mg_url_port(url)); - if (mg_aton(host, &c->rem)) { - // host is an IP address, do not fire name resolution - mg_connect_resolved(c); - } else { - // host is not an IP, send DNS resolution request - struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4; - mg_sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6); - } -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/event.c" -#endif - - - - - - -void mg_call(struct mg_connection *c, int ev, void *ev_data) { -#if MG_ENABLE_PROFILE - const char *names[] = { - "EV_ERROR", "EV_OPEN", "EV_POLL", "EV_RESOLVE", - "EV_CONNECT", "EV_ACCEPT", "EV_TLS_HS", "EV_READ", - "EV_WRITE", "EV_CLOSE", "EV_HTTP_MSG", "EV_HTTP_CHUNK", - "EV_WS_OPEN", "EV_WS_MSG", "EV_WS_CTL", "EV_MQTT_CMD", - "EV_MQTT_MSG", "EV_MQTT_OPEN", "EV_SNTP_TIME", "EV_USER"}; - if (ev != MG_EV_POLL && ev < (int) (sizeof(names) / sizeof(names[0]))) { - MG_PROF_ADD(c, names[ev]); - } -#endif - // Fire protocol handler first, user handler second. See #2559 - if (c->pfn != NULL) c->pfn(c, ev, ev_data); - if (c->fn != NULL) c->fn(c, ev, ev_data); -} - -void mg_error(struct mg_connection *c, const char *fmt, ...) { - char buf[64]; - va_list ap; - va_start(ap, fmt); - mg_vsnprintf(buf, sizeof(buf), fmt, &ap); - va_end(ap); - MG_ERROR(("%lu %ld %s", c->id, c->fd, buf)); - c->is_closing = 1; // Set is_closing before sending MG_EV_CALL - mg_call(c, MG_EV_ERROR, buf); // Let user handler override it -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/flash.c" -#endif - - - - - -#if MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM - -static char *s_addr; // Current address to write to -static size_t s_size; // Firmware size to flash. In-progress indicator -static uint32_t s_crc32; // Firmware checksum - -bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash) { - bool ok = false; - if (s_size) { - MG_ERROR(("OTA already in progress. Call mg_ota_end()")); - } else { - size_t half = flash->size / 2; - s_crc32 = 0; - s_addr = (char *) flash->start + half; - MG_DEBUG(("FW %lu bytes, max %lu", new_firmware_size, half)); - if (new_firmware_size < half) { - ok = true; - s_size = new_firmware_size; - MG_INFO(("Starting OTA, firmware size %lu", s_size)); - } else { - MG_ERROR(("Firmware %lu is too big to fit %lu", new_firmware_size, half)); - } - } - return ok; -} - -bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash) { - bool ok = false; - if (s_size == 0) { - MG_ERROR(("OTA is not started, call mg_ota_begin()")); - } else { - size_t len_aligned_down = MG_ROUND_DOWN(len, flash->align); - if (len_aligned_down) ok = flash->write_fn(s_addr, buf, len_aligned_down); - if (len_aligned_down < len) { - size_t left = len - len_aligned_down; - char tmp[flash->align]; - memset(tmp, 0xff, sizeof(tmp)); - memcpy(tmp, (char *) buf + len_aligned_down, left); - ok = flash->write_fn(s_addr + len_aligned_down, tmp, sizeof(tmp)); - } - s_crc32 = mg_crc32(s_crc32, (char *) buf, len); // Update CRC - MG_DEBUG(("%#x %p %lu -> %d", s_addr - len, buf, len, ok)); - s_addr += len; - } - return ok; -} - -bool mg_ota_flash_end(struct mg_flash *flash) { - char *base = (char *) flash->start + flash->size / 2; - bool ok = false; - if (s_size) { - size_t size = (size_t) (s_addr - base); - uint32_t crc32 = mg_crc32(0, base, s_size); - if (size == s_size && crc32 == s_crc32) ok = true; - MG_DEBUG(("CRC: %x/%x, size: %lu/%lu, status: %s", s_crc32, crc32, s_size, - size, ok ? "ok" : "fail")); - s_size = 0; - if (ok) ok = flash->swap_fn(); - } - MG_INFO(("Finishing OTA: %s", ok ? "ok" : "fail")); - return ok; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/fmt.c" -#endif - - - - -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int addexp(char *buf, int e, int sign) { - int n = 0; - buf[n++] = 'e'; - buf[n++] = (char) sign; - if (e > 400) return 0; - if (e < 10) buf[n++] = '0'; - if (e >= 100) buf[n++] = (char) (e / 100 + '0'), e -= 100 * (e / 100); - if (e >= 10) buf[n++] = (char) (e / 10 + '0'), e -= 10 * (e / 10); - buf[n++] = (char) (e + '0'); - return n; -} - -static int xisinf(double x) { - union { - double f; - uint64_t u; - } ieee754 = {x}; - return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && - ((unsigned) ieee754.u == 0); -} - -static int xisnan(double x) { - union { - double f; - uint64_t u; - } ieee754 = {x}; - return ((unsigned) (ieee754.u >> 32) & 0x7fffffff) + - ((unsigned) ieee754.u != 0) > - 0x7ff00000; -} - -static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { - char buf[40]; - int i, s = 0, n = 0, e = 0; - double t, mul, saved; - if (d == 0.0) return mg_snprintf(dst, dstlen, "%s", "0"); - if (xisinf(d)) return mg_snprintf(dst, dstlen, "%s", d > 0 ? "inf" : "-inf"); - if (xisnan(d)) return mg_snprintf(dst, dstlen, "%s", "nan"); - if (d < 0.0) d = -d, buf[s++] = '-'; - - // Round - saved = d; - if (tz) { - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; - } else { - mul = 0.1; - } - - while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0; - for (i = 0, t = mul * 5; i < width; i++) t /= 10.0; - - d += t; - - // Calculate exponent, and 'mul' for scientific representation - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++; - while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--; - // printf(" --> %g %d %g %g\n", saved, e, t, mul); - - if (tz && e >= width && width > 1) { - n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); - // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); - n += addexp(buf + s + n, e, '+'); - return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else if (tz && e <= -width && width > 1) { - n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); - // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); - n += addexp(buf + s + n, -e, '-'); - return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else { - int targ_width = width; - for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) { - int ch = (int) (d / t); - if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0'); - d -= ch * t; - t /= 10.0; - } - // printf(" --> [%g] -> %g %g (%d) [%.*s]\n", saved, d, t, n, s + n, buf); - if (n == 0) buf[s++] = '0'; - while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0; - if (s + n < (int) sizeof(buf)) buf[n + s++] = '.'; - // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf); - if (!tz && n > 0) targ_width = width + n; - for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < targ_width; i++) { - int ch = (int) (d / t); - buf[s + n++] = (char) (ch + '0'); - d -= ch * t; - t /= 10.0; - } - } - - while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes - if (tz && n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot - n += s; - if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; - buf[n] = '\0'; - return mg_snprintf(dst, dstlen, "%s", buf); -} - -static size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex) { - const char *letters = "0123456789abcdef"; - uint64_t v = (uint64_t) val; - size_t s = 0, n, i; - if (is_signed && val < 0) buf[s++] = '-', v = (uint64_t) (-val); - // This loop prints a number in reverse order. I guess this is because we - // write numbers from right to left: least significant digit comes last. - // Maybe because we use Arabic numbers, and Arabs write RTL? - if (is_hex) { - for (n = 0; v; v >>= 4) buf[s + n++] = letters[v & 15]; - } else { - for (n = 0; v; v /= 10) buf[s + n++] = letters[v % 10]; - } - // Reverse a string - for (i = 0; i < n / 2; i++) { - char t = buf[s + i]; - buf[s + i] = buf[s + n - i - 1], buf[s + n - i - 1] = t; - } - if (val == 0) buf[n++] = '0'; // Handle special case - return n + s; -} - -static size_t scpy(void (*out)(char, void *), void *ptr, char *buf, - size_t len) { - size_t i = 0; - while (i < len && buf[i] != '\0') out(buf[i++], ptr); - return i; -} - -size_t mg_xprintf(void (*out)(char, void *), void *ptr, const char *fmt, ...) { - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vxprintf(out, ptr, fmt, &ap); - va_end(ap); - return len; -} - -size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt, - va_list *ap) { - size_t i = 0, n = 0; - while (fmt[i] != '\0') { - if (fmt[i] == '%') { - size_t j, k, x = 0, is_long = 0, w = 0 /* width */, pr = ~0U /* prec */; - char pad = ' ', minus = 0, c = fmt[++i]; - if (c == '#') x++, c = fmt[++i]; - if (c == '-') minus++, c = fmt[++i]; - if (c == '0') pad = '0', c = fmt[++i]; - while (is_digit(c)) w *= 10, w += (size_t) (c - '0'), c = fmt[++i]; - if (c == '.') { - c = fmt[++i]; - if (c == '*') { - pr = (size_t) va_arg(*ap, int); - c = fmt[++i]; - } else { - pr = 0; - while (is_digit(c)) pr *= 10, pr += (size_t) (c - '0'), c = fmt[++i]; - } - } - while (c == 'h') c = fmt[++i]; // Treat h and hh as int - if (c == 'l') { - is_long++, c = fmt[++i]; - if (c == 'l') is_long++, c = fmt[++i]; - } - if (c == 'p') x = 1, is_long = 1; - if (c == 'd' || c == 'u' || c == 'x' || c == 'X' || c == 'p' || - c == 'g' || c == 'f') { - bool s = (c == 'd'), h = (c == 'x' || c == 'X' || c == 'p'); - char tmp[40]; - size_t xl = x ? 2 : 0; - if (c == 'g' || c == 'f') { - double v = va_arg(*ap, double); - if (pr == ~0U) pr = 6; - k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g'); - } else if (is_long == 2) { - int64_t v = va_arg(*ap, int64_t); - k = mg_lld(tmp, v, s, h); - } else if (is_long == 1) { - long v = va_arg(*ap, long); - k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned long) v, s, h); - } else { - int v = va_arg(*ap, int); - k = mg_lld(tmp, s ? (int64_t) v : (int64_t) (unsigned) v, s, h); - } - for (j = 0; j < xl && w > 0; j++) w--; - for (j = 0; pad == ' ' && !minus && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, (char *) "0x", xl); - for (j = 0; pad == '0' && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, tmp, k); - for (j = 0; pad == ' ' && minus && k < w && j + k < w; j++) - n += scpy(out, param, &pad, 1); - } else if (c == 'm' || c == 'M') { - mg_pm_t f = va_arg(*ap, mg_pm_t); - if (c == 'm') out('"', param); - n += f(out, param, ap); - if (c == 'm') n += 2, out('"', param); - } else if (c == 'c') { - int ch = va_arg(*ap, int); - out((char) ch, param); - n++; - } else if (c == 's') { - char *p = va_arg(*ap, char *); - if (pr == ~0U) pr = p == NULL ? 0 : strlen(p); - for (j = 0; !minus && pr < w && j + pr < w; j++) - n += scpy(out, param, &pad, 1); - n += scpy(out, param, p, pr); - for (j = 0; minus && pr < w && j + pr < w; j++) - n += scpy(out, param, &pad, 1); - } else if (c == '%') { - out('%', param); - n++; - } else { - out('%', param); - out(c, param); - n += 2; - } - i++; - } else { - out(fmt[i], param), n++, i++; - } - } - return n; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs.c" -#endif - - - - -struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags) { - struct mg_fd *fd = (struct mg_fd *) calloc(1, sizeof(*fd)); - if (fd != NULL) { - fd->fd = fs->op(path, flags); - fd->fs = fs; - if (fd->fd == NULL) { - free(fd); - fd = NULL; - } - } - return fd; -} - -void mg_fs_close(struct mg_fd *fd) { - if (fd != NULL) { - fd->fs->cl(fd->fd); - free(fd); - } -} - -struct mg_str mg_file_read(struct mg_fs *fs, const char *path) { - struct mg_str result = {NULL, 0}; - void *fp; - fs->st(path, &result.len, NULL); - if ((fp = fs->op(path, MG_FS_READ)) != NULL) { - result.buf = (char *) calloc(1, result.len + 1); - if (result.buf != NULL && - fs->rd(fp, (void *) result.buf, result.len) != result.len) { - free((void *) result.buf); - result.buf = NULL; - } - fs->cl(fp); - } - if (result.buf == NULL) result.len = 0; - return result; -} - -bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, - size_t len) { - bool result = false; - struct mg_fd *fd; - char tmp[MG_PATH_MAX]; - mg_snprintf(tmp, sizeof(tmp), "%s..%d", path, rand()); - if ((fd = mg_fs_open(fs, tmp, MG_FS_WRITE)) != NULL) { - result = fs->wr(fd->fd, buf, len) == len; - mg_fs_close(fd); - if (result) { - fs->rm(path); - fs->mv(tmp, path); - } else { - fs->rm(tmp); - } - } - return result; -} - -bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...) { - va_list ap; - char *data; - bool result = false; - va_start(ap, fmt); - data = mg_vmprintf(fmt, &ap); - va_end(ap); - result = mg_file_write(fs, path, data, strlen(data)); - free(data); - return result; -} - -// This helper function allows to scan a filesystem in a sequential way, -// without using callback function: -// char buf[100] = ""; -// while (mg_fs_ls(&mg_fs_posix, "./", buf, sizeof(buf))) { -// ... -static void mg_fs_ls_fn(const char *filename, void *param) { - struct mg_str *s = (struct mg_str *) param; - if (s->buf[0] == '\0') { - mg_snprintf((char *) s->buf, s->len, "%s", filename); - } else if (strcmp(s->buf, filename) == 0) { - ((char *) s->buf)[0] = '\0'; // Fetch next file - } -} - -bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len) { - struct mg_str s = {buf, len}; - fs->ls(path, mg_fs_ls_fn, &s); - return buf[0] != '\0'; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_fat.c" -#endif - - - -#if MG_ENABLE_FATFS -#include - -static int mg_days_from_epoch(int y, int m, int d) { - y -= m <= 2; - int era = y / 400; - int yoe = y - era * 400; - int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; - int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - return era * 146097 + doe - 719468; -} - -static time_t mg_timegm(const struct tm *t) { - int year = t->tm_year + 1900; - int month = t->tm_mon; // 0-11 - if (month > 11) { - year += month / 12; - month %= 12; - } else if (month < 0) { - int years_diff = (11 - month) / 12; - year -= years_diff; - month += 12 * years_diff; - } - int x = mg_days_from_epoch(year, month + 1, t->tm_mday); - return 60 * (60 * (24L * x + t->tm_hour) + t->tm_min) + t->tm_sec; -} - -static time_t ff_time_to_epoch(uint16_t fdate, uint16_t ftime) { - struct tm tm; - memset(&tm, 0, sizeof(struct tm)); - tm.tm_sec = (ftime << 1) & 0x3e; - tm.tm_min = ((ftime >> 5) & 0x3f); - tm.tm_hour = ((ftime >> 11) & 0x1f); - tm.tm_mday = (fdate & 0x1f); - tm.tm_mon = ((fdate >> 5) & 0x0f) - 1; - tm.tm_year = ((fdate >> 9) & 0x7f) + 80; - return mg_timegm(&tm); -} - -static int ff_stat(const char *path, size_t *size, time_t *mtime) { - FILINFO fi; - if (path[0] == '\0') { - if (size) *size = 0; - if (mtime) *mtime = 0; - return MG_FS_DIR; - } else if (f_stat(path, &fi) == 0) { - if (size) *size = (size_t) fi.fsize; - if (mtime) *mtime = ff_time_to_epoch(fi.fdate, fi.ftime); - return MG_FS_READ | MG_FS_WRITE | ((fi.fattrib & AM_DIR) ? MG_FS_DIR : 0); - } else { - return 0; - } -} - -static void ff_list(const char *dir, void (*fn)(const char *, void *), - void *userdata) { - DIR d; - FILINFO fi; - if (f_opendir(&d, dir) == FR_OK) { - while (f_readdir(&d, &fi) == FR_OK && fi.fname[0] != '\0') { - if (!strcmp(fi.fname, ".") || !strcmp(fi.fname, "..")) continue; - fn(fi.fname, userdata); - } - f_closedir(&d); - } -} - -static void *ff_open(const char *path, int flags) { - FIL f; - unsigned char mode = FA_READ; - if (flags & MG_FS_WRITE) mode |= FA_WRITE | FA_OPEN_ALWAYS | FA_OPEN_APPEND; - if (f_open(&f, path, mode) == 0) { - FIL *fp; - if ((fp = calloc(1, sizeof(*fp))) != NULL) { - memcpy(fp, &f, sizeof(*fp)); - return fp; - } - } - return NULL; -} - -static void ff_close(void *fp) { - if (fp != NULL) { - f_close((FIL *) fp); - free(fp); - } -} - -static size_t ff_read(void *fp, void *buf, size_t len) { - UINT n = 0, misalign = ((size_t) buf) & 3; - if (misalign) { - char aligned[4]; - f_read((FIL *) fp, aligned, len > misalign ? misalign : len, &n); - memcpy(buf, aligned, n); - } else { - f_read((FIL *) fp, buf, len, &n); - } - return n; -} - -static size_t ff_write(void *fp, const void *buf, size_t len) { - UINT n = 0; - return f_write((FIL *) fp, (char *) buf, len, &n) == FR_OK ? n : 0; -} - -static size_t ff_seek(void *fp, size_t offset) { - f_lseek((FIL *) fp, offset); - return offset; -} - -static bool ff_rename(const char *from, const char *to) { - return f_rename(from, to) == FR_OK; -} - -static bool ff_remove(const char *path) { - return f_unlink(path) == FR_OK; -} - -static bool ff_mkdir(const char *path) { - return f_mkdir(path) == FR_OK; -} - -struct mg_fs mg_fs_fat = {ff_stat, ff_list, ff_open, ff_close, ff_read, - ff_write, ff_seek, ff_rename, ff_remove, ff_mkdir}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_packed.c" -#endif - - - - -struct packed_file { - const char *data; - size_t size; - size_t pos; -}; - -#if MG_ENABLE_PACKED_FS -#else -const char *mg_unpack(const char *path, size_t *size, time_t *mtime) { - if (size != NULL) *size = 0; - if (mtime != NULL) *mtime = 0; - (void) path; - return NULL; -} -const char *mg_unlist(size_t no) { - (void) no; - return NULL; -} -#endif - -struct mg_str mg_unpacked(const char *path) { - size_t len = 0; - const char *buf = mg_unpack(path, &len, NULL); - return mg_str_n(buf, len); -} - -static int is_dir_prefix(const char *prefix, size_t n, const char *path) { - // MG_INFO(("[%.*s] [%s] %c", (int) n, prefix, path, path[n])); - return n < strlen(path) && strncmp(prefix, path, n) == 0 && - (n == 0 || path[n] == '/' || path[n - 1] == '/'); -} - -static int packed_stat(const char *path, size_t *size, time_t *mtime) { - const char *p; - size_t i, n = strlen(path); - if (mg_unpack(path, size, mtime)) return MG_FS_READ; // Regular file - // Scan all files. If `path` is a dir prefix for any of them, it's a dir - for (i = 0; (p = mg_unlist(i)) != NULL; i++) { - if (is_dir_prefix(path, n, p)) return MG_FS_DIR; - } - return 0; -} - -static void packed_list(const char *dir, void (*fn)(const char *, void *), - void *userdata) { - char buf[MG_PATH_MAX], tmp[sizeof(buf)]; - const char *path, *begin, *end; - size_t i, n = strlen(dir); - tmp[0] = '\0'; // Previously listed entry - for (i = 0; (path = mg_unlist(i)) != NULL; i++) { - if (!is_dir_prefix(dir, n, path)) continue; - begin = &path[n + 1]; - end = strchr(begin, '/'); - if (end == NULL) end = begin + strlen(begin); - mg_snprintf(buf, sizeof(buf), "%.*s", (int) (end - begin), begin); - buf[sizeof(buf) - 1] = '\0'; - // If this entry has been already listed, skip - // NOTE: we're assuming that file list is sorted alphabetically - if (strcmp(buf, tmp) == 0) continue; - fn(buf, userdata); // Not yet listed, call user function - strcpy(tmp, buf); // And save this entry as listed - } -} - -static void *packed_open(const char *path, int flags) { - size_t size = 0; - const char *data = mg_unpack(path, &size, NULL); - struct packed_file *fp = NULL; - if (data == NULL) return NULL; - if (flags & MG_FS_WRITE) return NULL; - if ((fp = (struct packed_file *) calloc(1, sizeof(*fp))) != NULL) { - fp->size = size; - fp->data = data; - } - return (void *) fp; -} - -static void packed_close(void *fp) { - if (fp != NULL) free(fp); -} - -static size_t packed_read(void *fd, void *buf, size_t len) { - struct packed_file *fp = (struct packed_file *) fd; - if (fp->pos + len > fp->size) len = fp->size - fp->pos; - memcpy(buf, &fp->data[fp->pos], len); - fp->pos += len; - return len; -} - -static size_t packed_write(void *fd, const void *buf, size_t len) { - (void) fd, (void) buf, (void) len; - return 0; -} - -static size_t packed_seek(void *fd, size_t offset) { - struct packed_file *fp = (struct packed_file *) fd; - fp->pos = offset; - if (fp->pos > fp->size) fp->pos = fp->size; - return fp->pos; -} - -static bool packed_rename(const char *from, const char *to) { - (void) from, (void) to; - return false; -} - -static bool packed_remove(const char *path) { - (void) path; - return false; -} - -static bool packed_mkdir(const char *path) { - (void) path; - return false; -} - -struct mg_fs mg_fs_packed = { - packed_stat, packed_list, packed_open, packed_close, packed_read, - packed_write, packed_seek, packed_rename, packed_remove, packed_mkdir}; - -#ifdef MG_ENABLE_LINES -#line 1 "src/fs_posix.c" -#endif - - -#if MG_ENABLE_POSIX_FS - -#ifndef MG_STAT_STRUCT -#define MG_STAT_STRUCT stat -#endif - -#ifndef MG_STAT_FUNC -#define MG_STAT_FUNC stat -#endif - -static int p_stat(const char *path, size_t *size, time_t *mtime) { -#if !defined(S_ISDIR) - MG_ERROR(("stat() API is not supported. %p %p %p", path, size, mtime)); - return 0; -#else -#if MG_ARCH == MG_ARCH_WIN32 - struct _stati64 st; - wchar_t tmp[MG_PATH_MAX]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, tmp, sizeof(tmp) / sizeof(tmp[0])); - if (_wstati64(tmp, &st) != 0) return 0; - // If path is a symlink, windows reports 0 in st.st_size. - // Get a real file size by opening it and jumping to the end - if (st.st_size == 0 && (st.st_mode & _S_IFREG)) { - FILE *fp = _wfopen(tmp, L"rb"); - if (fp != NULL) { - fseek(fp, 0, SEEK_END); - if (ftell(fp) > 0) st.st_size = ftell(fp); // Use _ftelli64 on win10+ - fclose(fp); - } - } -#else - struct MG_STAT_STRUCT st; - if (MG_STAT_FUNC(path, &st) != 0) return 0; -#endif - if (size) *size = (size_t) st.st_size; - if (mtime) *mtime = st.st_mtime; - return MG_FS_READ | MG_FS_WRITE | (S_ISDIR(st.st_mode) ? MG_FS_DIR : 0); -#endif -} - -#if MG_ARCH == MG_ARCH_WIN32 -struct dirent { - char d_name[MAX_PATH]; -}; - -typedef struct win32_dir { - HANDLE handle; - WIN32_FIND_DATAW info; - struct dirent result; -} DIR; - -#if 0 -int gettimeofday(struct timeval *tv, void *tz) { - FILETIME ft; - unsigned __int64 tmpres = 0; - - if (tv != NULL) { - GetSystemTimeAsFileTime(&ft); - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - tmpres /= 10; // convert into microseconds - tmpres -= (int64_t) 11644473600000000; - tv->tv_sec = (long) (tmpres / 1000000UL); - tv->tv_usec = (long) (tmpres % 1000000UL); - } - (void) tz; - return 0; -} -#endif - -static int to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { - int ret; - char buf[MAX_PATH * 2], buf2[MAX_PATH * 2], *p; - strncpy(buf, path, sizeof(buf)); - buf[sizeof(buf) - 1] = '\0'; - // Trim trailing slashes. Leave backslash for paths like "X:\" - p = buf + strlen(buf) - 1; - while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; - memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); - ret = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); - // Convert back to Unicode. If doubly-converted string does not match the - // original, something is fishy, reject. - WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), - NULL, NULL); - if (strcmp(buf, buf2) != 0) { - wbuf[0] = L'\0'; - ret = 0; - } - return ret; -} - -DIR *opendir(const char *name) { - DIR *d = NULL; - wchar_t wpath[MAX_PATH]; - DWORD attrs; - - if (name == NULL) { - SetLastError(ERROR_BAD_ARGUMENTS); - } else if ((d = (DIR *) calloc(1, sizeof(*d))) == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } else { - to_wchar(name, wpath, sizeof(wpath) / sizeof(wpath[0])); - attrs = GetFileAttributesW(wpath); - if (attrs != 0Xffffffff && (attrs & FILE_ATTRIBUTE_DIRECTORY)) { - (void) wcscat(wpath, L"\\*"); - d->handle = FindFirstFileW(wpath, &d->info); - d->result.d_name[0] = '\0'; - } else { - free(d); - d = NULL; - } - } - return d; -} - -int closedir(DIR *d) { - int result = 0; - if (d != NULL) { - if (d->handle != INVALID_HANDLE_VALUE) - result = FindClose(d->handle) ? 0 : -1; - free(d); - } else { - result = -1; - SetLastError(ERROR_BAD_ARGUMENTS); - } - return result; -} - -struct dirent *readdir(DIR *d) { - struct dirent *result = NULL; - if (d != NULL) { - memset(&d->result, 0, sizeof(d->result)); - if (d->handle != INVALID_HANDLE_VALUE) { - result = &d->result; - WideCharToMultiByte(CP_UTF8, 0, d->info.cFileName, -1, result->d_name, - sizeof(result->d_name), NULL, NULL); - if (!FindNextFileW(d->handle, &d->info)) { - FindClose(d->handle); - d->handle = INVALID_HANDLE_VALUE; - } - } else { - SetLastError(ERROR_FILE_NOT_FOUND); - } - } else { - SetLastError(ERROR_BAD_ARGUMENTS); - } - return result; -} -#endif - -static void p_list(const char *dir, void (*fn)(const char *, void *), - void *userdata) { -#if MG_ENABLE_DIRLIST - struct dirent *dp; - DIR *dirp; - if ((dirp = (opendir(dir))) == NULL) return; - while ((dp = readdir(dirp)) != NULL) { - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue; - fn(dp->d_name, userdata); - } - closedir(dirp); -#else - (void) dir, (void) fn, (void) userdata; -#endif -} - -static void *p_open(const char *path, int flags) { -#if MG_ARCH == MG_ARCH_WIN32 - const char *mode = flags == MG_FS_READ ? "rb" : "a+b"; - wchar_t b1[MG_PATH_MAX], b2[10]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, b1, sizeof(b1) / sizeof(b1[0])); - MultiByteToWideChar(CP_UTF8, 0, mode, -1, b2, sizeof(b2) / sizeof(b2[0])); - return (void *) _wfopen(b1, b2); -#else - const char *mode = flags == MG_FS_READ ? "rbe" : "a+be"; // e for CLOEXEC - return (void *) fopen(path, mode); -#endif -} - -static void p_close(void *fp) { - fclose((FILE *) fp); -} - -static size_t p_read(void *fp, void *buf, size_t len) { - return fread(buf, 1, len, (FILE *) fp); -} - -static size_t p_write(void *fp, const void *buf, size_t len) { - return fwrite(buf, 1, len, (FILE *) fp); -} - -static size_t p_seek(void *fp, size_t offset) { -#if (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) || \ - (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ - (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) - if (fseeko((FILE *) fp, (off_t) offset, SEEK_SET) != 0) (void) 0; -#else - if (fseek((FILE *) fp, (long) offset, SEEK_SET) != 0) (void) 0; -#endif - return (size_t) ftell((FILE *) fp); -} - -static bool p_rename(const char *from, const char *to) { - return rename(from, to) == 0; -} - -static bool p_remove(const char *path) { - return remove(path) == 0; -} - -static bool p_mkdir(const char *path) { - return mkdir(path, 0775) == 0; -} - -#else - -static int p_stat(const char *path, size_t *size, time_t *mtime) { - (void) path, (void) size, (void) mtime; - return 0; -} -static void p_list(const char *path, void (*fn)(const char *, void *), - void *userdata) { - (void) path, (void) fn, (void) userdata; -} -static void *p_open(const char *path, int flags) { - (void) path, (void) flags; - return NULL; -} -static void p_close(void *fp) { - (void) fp; -} -static size_t p_read(void *fd, void *buf, size_t len) { - (void) fd, (void) buf, (void) len; - return 0; -} -static size_t p_write(void *fd, const void *buf, size_t len) { - (void) fd, (void) buf, (void) len; - return 0; -} -static size_t p_seek(void *fd, size_t offset) { - (void) fd, (void) offset; - return (size_t) ~0; -} -static bool p_rename(const char *from, const char *to) { - (void) from, (void) to; - return false; -} -static bool p_remove(const char *path) { - (void) path; - return false; -} -static bool p_mkdir(const char *path) { - (void) path; - return false; -} -#endif - -struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read, - p_write, p_seek, p_rename, p_remove, p_mkdir}; - -#ifdef MG_ENABLE_LINES -#line 1 "src/http.c" -#endif - - - - - - - - - - - - - -static int mg_ncasecmp(const char *s1, const char *s2, size_t len) { - int diff = 0; - if (len > 0) do { - int c = *s1++, d = *s2++; - if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; - if (d >= 'A' && d <= 'Z') d += 'a' - 'A'; - diff = c - d; - } while (diff == 0 && s1[-1] != '\0' && --len > 0); - return diff; -} - -bool mg_to_size_t(struct mg_str str, size_t *val); -bool mg_to_size_t(struct mg_str str, size_t *val) { - size_t i = 0, max = (size_t) -1, max2 = max / 10, result = 0, ndigits = 0; - while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++; - if (i < str.len && str.buf[i] == '-') return false; - while (i < str.len && str.buf[i] >= '0' && str.buf[i] <= '9') { - size_t digit = (size_t) (str.buf[i] - '0'); - if (result > max2) return false; // Overflow - result *= 10; - if (result > max - digit) return false; // Overflow - result += digit; - i++, ndigits++; - } - while (i < str.len && (str.buf[i] == ' ' || str.buf[i] == '\t')) i++; - if (ndigits == 0) return false; // #2322: Content-Length = 1 * DIGIT - if (i != str.len) return false; // Ditto - *val = (size_t) result; - return true; -} - -// Chunk deletion marker is the MSB in the "processed" counter -#define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1)) - -// Multipart POST example: -// --xyz -// Content-Disposition: form-data; name="val" -// -// abcdef -// --xyz -// Content-Disposition: form-data; name="foo"; filename="a.txt" -// Content-Type: text/plain -// -// hello world -// -// --xyz-- -size_t mg_http_next_multipart(struct mg_str body, size_t ofs, - struct mg_http_part *part) { - struct mg_str cd = mg_str_n("Content-Disposition", 19); - const char *s = body.buf; - size_t b = ofs, h1, h2, b1, b2, max = body.len; - - // Init part params - if (part != NULL) part->name = part->filename = part->body = mg_str_n(0, 0); - - // Skip boundary - while (b + 2 < max && s[b] != '\r' && s[b + 1] != '\n') b++; - if (b <= ofs || b + 2 >= max) return 0; - // MG_INFO(("B: %zu %zu [%.*s]", ofs, b - ofs, (int) (b - ofs), s)); - - // Skip headers - h1 = h2 = b + 2; - for (;;) { - while (h2 + 2 < max && s[h2] != '\r' && s[h2 + 1] != '\n') h2++; - if (h2 == h1) break; - if (h2 + 2 >= max) return 0; - // MG_INFO(("Header: [%.*s]", (int) (h2 - h1), &s[h1])); - if (part != NULL && h1 + cd.len + 2 < h2 && s[h1 + cd.len] == ':' && - mg_ncasecmp(&s[h1], cd.buf, cd.len) == 0) { - struct mg_str v = mg_str_n(&s[h1 + cd.len + 2], h2 - (h1 + cd.len + 2)); - part->name = mg_http_get_header_var(v, mg_str_n("name", 4)); - part->filename = mg_http_get_header_var(v, mg_str_n("filename", 8)); - } - h1 = h2 = h2 + 2; - } - b1 = b2 = h2 + 2; - while (b2 + 2 + (b - ofs) + 2 < max && !(s[b2] == '\r' && s[b2 + 1] == '\n' && - memcmp(&s[b2 + 2], s, b - ofs) == 0)) - b2++; - - if (b2 + 2 >= max) return 0; - if (part != NULL) part->body = mg_str_n(&s[b1], b2 - b1); - // MG_INFO(("Body: [%.*s]", (int) (b2 - b1), &s[b1])); - return b2 + 2; -} - -void mg_http_bauth(struct mg_connection *c, const char *user, - const char *pass) { - struct mg_str u = mg_str(user), p = mg_str(pass); - size_t need = c->send.len + 36 + (u.len + p.len) * 2; - if (c->send.size < need) mg_iobuf_resize(&c->send, need); - if (c->send.size >= need) { - size_t i, n = 0; - char *buf = (char *) &c->send.buf[c->send.len]; - memcpy(buf, "Authorization: Basic ", 21); // DON'T use mg_send! - for (i = 0; i < u.len; i++) { - n = mg_base64_update(((unsigned char *) u.buf)[i], buf + 21, n); - } - if (p.len > 0) { - n = mg_base64_update(':', buf + 21, n); - for (i = 0; i < p.len; i++) { - n = mg_base64_update(((unsigned char *) p.buf)[i], buf + 21, n); - } - } - n = mg_base64_final(buf + 21, n); - c->send.len += 21 + (size_t) n + 2; - memcpy(&c->send.buf[c->send.len - 2], "\r\n", 2); - } else { - MG_ERROR(("%lu oom %d->%d ", c->id, (int) c->send.size, (int) need)); - } -} - -struct mg_str mg_http_var(struct mg_str buf, struct mg_str name) { - struct mg_str entry, k, v, result = mg_str_n(NULL, 0); - while (mg_span(buf, &entry, &buf, '&')) { - if (mg_span(entry, &k, &v, '=') && name.len == k.len && - mg_ncasecmp(name.buf, k.buf, k.len) == 0) { - result = v; - break; - } - } - return result; -} - -int mg_http_get_var(const struct mg_str *buf, const char *name, char *dst, - size_t dst_len) { - int len; - if (dst != NULL && dst_len > 0) { - dst[0] = '\0'; // If destination buffer is valid, always nul-terminate it - } - if (dst == NULL || dst_len == 0) { - len = -2; // Bad destination - } else if (buf->buf == NULL || name == NULL || buf->len == 0) { - len = -1; // Bad source - } else { - struct mg_str v = mg_http_var(*buf, mg_str(name)); - if (v.buf == NULL) { - len = -4; // Name does not exist - } else { - len = mg_url_decode(v.buf, v.len, dst, dst_len, 1); - if (len < 0) len = -3; // Failed to decode - } - } - return len; -} - -static bool isx(int c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -int mg_url_decode(const char *src, size_t src_len, char *dst, size_t dst_len, - int is_form_url_encoded) { - size_t i, j; - for (i = j = 0; i < src_len && j + 1 < dst_len; i++, j++) { - if (src[i] == '%') { - // Use `i + 2 < src_len`, not `i < src_len - 2`, note small src_len - if (i + 2 < src_len && isx(src[i + 1]) && isx(src[i + 2])) { - mg_str_to_num(mg_str_n(src + i + 1, 2), 16, &dst[j], sizeof(uint8_t)); - i += 2; - } else { - return -1; - } - } else if (is_form_url_encoded && src[i] == '+') { - dst[j] = ' '; - } else { - dst[j] = src[i]; - } - } - if (j < dst_len) dst[j] = '\0'; // Null-terminate the destination - return i >= src_len && j < dst_len ? (int) j : -1; -} - -static bool isok(uint8_t c) { - return c == '\n' || c == '\r' || c == '\t' || c >= ' '; -} - -int mg_http_get_request_len(const unsigned char *buf, size_t buf_len) { - size_t i; - for (i = 0; i < buf_len; i++) { - if (!isok(buf[i])) return -1; - if ((i > 0 && buf[i] == '\n' && buf[i - 1] == '\n') || - (i > 3 && buf[i] == '\n' && buf[i - 1] == '\r' && buf[i - 2] == '\n')) - return (int) i + 1; - } - return 0; -} -struct mg_str *mg_http_get_header(struct mg_http_message *h, const char *name) { - size_t i, n = strlen(name), max = sizeof(h->headers) / sizeof(h->headers[0]); - for (i = 0; i < max && h->headers[i].name.len > 0; i++) { - struct mg_str *k = &h->headers[i].name, *v = &h->headers[i].value; - if (n == k->len && mg_ncasecmp(k->buf, name, n) == 0) return v; - } - return NULL; -} - -// Is it a valid utf-8 continuation byte -static bool vcb(uint8_t c) { - return (c & 0xc0) == 0x80; -} - -// Get character length (valid utf-8). Used to parse method, URI, headers -static size_t clen(const char *s, const char *end) { - const unsigned char *u = (unsigned char *) s, c = *u; - long n = (long) (end - s); - if (c > ' ' && c < '~') return 1; // Usual ascii printed char - if ((c & 0xe0) == 0xc0 && n > 1 && vcb(u[1])) return 2; // 2-byte UTF8 - if ((c & 0xf0) == 0xe0 && n > 2 && vcb(u[1]) && vcb(u[2])) return 3; - if ((c & 0xf8) == 0xf0 && n > 3 && vcb(u[1]) && vcb(u[2]) && vcb(u[3])) - return 4; - return 0; -} - -// Skip until the newline. Return advanced `s`, or NULL on error -static const char *skiptorn(const char *s, const char *end, struct mg_str *v) { - v->buf = (char *) s; - while (s < end && s[0] != '\n' && s[0] != '\r') s++, v->len++; // To newline - if (s >= end || (s[0] == '\r' && s[1] != '\n')) return NULL; // Stray \r - if (s < end && s[0] == '\r') s++; // Skip \r - if (s >= end || *s++ != '\n') return NULL; // Skip \n - return s; -} - -static bool mg_http_parse_headers(const char *s, const char *end, - struct mg_http_header *h, size_t max_hdrs) { - size_t i, n; - for (i = 0; i < max_hdrs; i++) { - struct mg_str k = {NULL, 0}, v = {NULL, 0}; - if (s >= end) return false; - if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n')) break; - k.buf = (char *) s; - while (s < end && s[0] != ':' && (n = clen(s, end)) > 0) s += n, k.len += n; - if (k.len == 0) return false; // Empty name - if (s >= end || clen(s, end) == 0) return false; // Invalid UTF-8 - if (*s++ != ':') return false; // Invalid, not followed by : - // if (clen(s, end) == 0) return false; // Invalid UTF-8 - while (s < end && (s[0] == ' ' || s[0] == '\t')) s++; // Skip spaces - if ((s = skiptorn(s, end, &v)) == NULL) return false; - while (v.len > 0 && (v.buf[v.len - 1] == ' ' || v.buf[v.len - 1] == '\t')) { - v.len--; // Trim spaces - } - // MG_INFO(("--HH [%.*s] [%.*s]", (int) k.len, k.buf, (int) v.len, v.buf)); - h[i].name = k, h[i].value = v; // Success. Assign values - } - return true; -} - -int mg_http_parse(const char *s, size_t len, struct mg_http_message *hm) { - int is_response, req_len = mg_http_get_request_len((unsigned char *) s, len); - const char *end = s == NULL ? NULL : s + req_len, *qs; // Cannot add to NULL - const struct mg_str *cl; - size_t n; - bool version_prefix_valid; - - memset(hm, 0, sizeof(*hm)); - if (req_len <= 0) return req_len; - - hm->message.buf = hm->head.buf = (char *) s; - hm->body.buf = (char *) end; - hm->head.len = (size_t) req_len; - hm->message.len = hm->body.len = (size_t) -1; // Set body length to infinite - - // Parse request line - hm->method.buf = (char *) s; - while (s < end && (n = clen(s, end)) > 0) s += n, hm->method.len += n; - while (s < end && s[0] == ' ') s++; // Skip spaces - hm->uri.buf = (char *) s; - while (s < end && (n = clen(s, end)) > 0) s += n, hm->uri.len += n; - while (s < end && s[0] == ' ') s++; // Skip spaces - is_response = hm->method.len > 5 && - (mg_ncasecmp(hm->method.buf, "HTTP/", 5) == 0); - if ((s = skiptorn(s, end, &hm->proto)) == NULL) return false; - // If we're given a version, check that it is HTTP/x.x - version_prefix_valid = hm->proto.len > 5 && - (mg_ncasecmp(hm->proto.buf, "HTTP/", 5) == 0); - if (!is_response && hm->proto.len > 0 && - (!version_prefix_valid || hm->proto.len != 8 || - (hm->proto.buf[5] < '0' || hm->proto.buf[5] > '9') || - (hm->proto.buf[6] != '.') || - (hm->proto.buf[7] < '0' || hm->proto.buf[7] > '9'))) { - return -1; - } - - // If URI contains '?' character, setup query string - if ((qs = (const char *) memchr(hm->uri.buf, '?', hm->uri.len)) != NULL) { - hm->query.buf = (char *) qs + 1; - hm->query.len = (size_t) (&hm->uri.buf[hm->uri.len] - (qs + 1)); - hm->uri.len = (size_t) (qs - hm->uri.buf); - } - - // Sanity check. Allow protocol/reason to be empty - // Do this check after hm->method.len and hm->uri.len are finalised - if (hm->method.len == 0 || hm->uri.len == 0) return -1; - - if (!mg_http_parse_headers(s, end, hm->headers, - sizeof(hm->headers) / sizeof(hm->headers[0]))) - return -1; // error when parsing - if ((cl = mg_http_get_header(hm, "Content-Length")) != NULL) { - if (mg_to_size_t(*cl, &hm->body.len) == false) return -1; - hm->message.len = (size_t) req_len + hm->body.len; - } - - // mg_http_parse() is used to parse both HTTP requests and HTTP - // responses. If HTTP response does not have Content-Length set, then - // body is read until socket is closed, i.e. body.len is infinite (~0). - // - // For HTTP requests though, according to - // http://tools.ietf.org/html/rfc7231#section-8.1.3, - // only POST and PUT methods have defined body semantics. - // Therefore, if Content-Length is not specified and methods are - // not one of PUT or POST, set body length to 0. - // - // So, if it is HTTP request, and Content-Length is not set, - // and method is not (PUT or POST) then reset body length to zero. - if (hm->body.len == (size_t) ~0 && !is_response && - mg_strcasecmp(hm->method, mg_str("PUT")) != 0 && - mg_strcasecmp(hm->method, mg_str("POST")) != 0) { - hm->body.len = 0; - hm->message.len = (size_t) req_len; - } - - // The 204 (No content) responses also have 0 body length - if (hm->body.len == (size_t) ~0 && is_response && - mg_strcasecmp(hm->uri, mg_str("204")) == 0) { - hm->body.len = 0; - hm->message.len = (size_t) req_len; - } - if (hm->message.len < (size_t) req_len) return -1; // Overflow protection - - return req_len; -} - -static void mg_http_vprintf_chunk(struct mg_connection *c, const char *fmt, - va_list *ap) { - size_t len = c->send.len; - mg_send(c, " \r\n", 10); - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - if (c->send.len >= len + 10) { - mg_snprintf((char *) c->send.buf + len, 9, "%08lx", c->send.len - len - 10); - c->send.buf[len + 8] = '\r'; - if (c->send.len == len + 10) c->is_resp = 0; // Last chunk, reset marker - } - mg_send(c, "\r\n", 2); -} - -void mg_http_printf_chunk(struct mg_connection *c, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_http_vprintf_chunk(c, fmt, &ap); - va_end(ap); -} - -void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len) { - mg_printf(c, "%lx\r\n", (unsigned long) len); - mg_send(c, buf, len); - mg_send(c, "\r\n", 2); - if (len == 0) c->is_resp = 0; -} - -// clang-format off -static const char *mg_http_status_code_str(int status_code) { - switch (status_code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 208: return "Already Reported"; - case 226: return "IM Used"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 308: return "Permanent Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Payload Too Large"; - case 414: return "Request-URI Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 418: return "I'm a teapot"; - case 421: return "Misdirected Request"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 426: return "Upgrade Required"; - case 428: return "Precondition Required"; - case 429: return "Too Many Requests"; - case 431: return "Request Header Fields Too Large"; - case 444: return "Connection Closed Without Response"; - case 451: return "Unavailable For Legal Reasons"; - case 499: return "Client Closed Request"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "HTTP Version Not Supported"; - case 506: return "Variant Also Negotiates"; - case 507: return "Insufficient Storage"; - case 508: return "Loop Detected"; - case 510: return "Not Extended"; - case 511: return "Network Authentication Required"; - case 599: return "Network Connect Timeout Error"; - default: return ""; - } -} -// clang-format on - -void mg_http_reply(struct mg_connection *c, int code, const char *headers, - const char *fmt, ...) { - va_list ap; - size_t len; - mg_printf(c, "HTTP/1.1 %d %s\r\n%sContent-Length: \r\n\r\n", code, - mg_http_status_code_str(code), headers == NULL ? "" : headers); - len = c->send.len; - va_start(ap, fmt); - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, &ap); - va_end(ap); - if (c->send.len > 16) { - size_t n = mg_snprintf((char *) &c->send.buf[len - 15], 11, "%-10lu", - (unsigned long) (c->send.len - len)); - c->send.buf[len - 15 + n] = ' '; // Change ending 0 to space - } - c->is_resp = 0; -} - -static void http_cb(struct mg_connection *, int, void *); -static void restore_http_cb(struct mg_connection *c) { - mg_fs_close((struct mg_fd *) c->pfn_data); - c->pfn_data = NULL; - c->pfn = http_cb; - c->is_resp = 0; -} - -char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime); -char *mg_http_etag(char *buf, size_t len, size_t size, time_t mtime) { - mg_snprintf(buf, len, "\"%lld.%lld\"", (int64_t) mtime, (int64_t) size); - return buf; -} - -static void static_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_WRITE || ev == MG_EV_POLL) { - struct mg_fd *fd = (struct mg_fd *) c->pfn_data; - // Read to send IO buffer directly, avoid extra on-stack buffer - size_t n, max = MG_IO_SIZE, space; - size_t *cl = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / - sizeof(size_t) * sizeof(size_t)]; - if (c->send.size < max) mg_iobuf_resize(&c->send, max); - if (c->send.len >= c->send.size) return; // Rate limit - if ((space = c->send.size - c->send.len) > *cl) space = *cl; - n = fd->fs->rd(fd->fd, c->send.buf + c->send.len, space); - c->send.len += n; - *cl -= n; - if (n == 0) restore_http_cb(c); - } else if (ev == MG_EV_CLOSE) { - restore_http_cb(c); - } - (void) ev_data; -} - -// Known mime types. Keep it outside guess_content_type() function, since -// some environments don't like it defined there. -// clang-format off -#define MG_C_STR(a) { (char *) (a), sizeof(a) - 1 } -static struct mg_str s_known_types[] = { - MG_C_STR("html"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("htm"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("css"), MG_C_STR("text/css; charset=utf-8"), - MG_C_STR("js"), MG_C_STR("text/javascript; charset=utf-8"), - MG_C_STR("gif"), MG_C_STR("image/gif"), - MG_C_STR("png"), MG_C_STR("image/png"), - MG_C_STR("jpg"), MG_C_STR("image/jpeg"), - MG_C_STR("jpeg"), MG_C_STR("image/jpeg"), - MG_C_STR("woff"), MG_C_STR("font/woff"), - MG_C_STR("ttf"), MG_C_STR("font/ttf"), - MG_C_STR("svg"), MG_C_STR("image/svg+xml"), - MG_C_STR("txt"), MG_C_STR("text/plain; charset=utf-8"), - MG_C_STR("avi"), MG_C_STR("video/x-msvideo"), - MG_C_STR("csv"), MG_C_STR("text/csv"), - MG_C_STR("doc"), MG_C_STR("application/msword"), - MG_C_STR("exe"), MG_C_STR("application/octet-stream"), - MG_C_STR("gz"), MG_C_STR("application/gzip"), - MG_C_STR("ico"), MG_C_STR("image/x-icon"), - MG_C_STR("json"), MG_C_STR("application/json"), - MG_C_STR("mov"), MG_C_STR("video/quicktime"), - MG_C_STR("mp3"), MG_C_STR("audio/mpeg"), - MG_C_STR("mp4"), MG_C_STR("video/mp4"), - MG_C_STR("mpeg"), MG_C_STR("video/mpeg"), - MG_C_STR("pdf"), MG_C_STR("application/pdf"), - MG_C_STR("shtml"), MG_C_STR("text/html; charset=utf-8"), - MG_C_STR("tgz"), MG_C_STR("application/tar-gz"), - MG_C_STR("wav"), MG_C_STR("audio/wav"), - MG_C_STR("webp"), MG_C_STR("image/webp"), - MG_C_STR("zip"), MG_C_STR("application/zip"), - MG_C_STR("3gp"), MG_C_STR("video/3gpp"), - {0, 0}, -}; -// clang-format on - -static struct mg_str guess_content_type(struct mg_str path, const char *extra) { - struct mg_str entry, k, v, s = mg_str(extra), asterisk = mg_str_n("*", 1); - size_t i = 0; - - // Shrink path to its extension only - while (i < path.len && path.buf[path.len - i - 1] != '.') i++; - path.buf += path.len - i; - path.len = i; - - // Process user-provided mime type overrides, if any - while (mg_span(s, &entry, &s, ',')) { - if (mg_span(entry, &k, &v, '=') && - (mg_strcmp(asterisk, k) == 0 || mg_strcmp(path, k) == 0)) - return v; - } - - // Process built-in mime types - for (i = 0; s_known_types[i].buf != NULL; i += 2) { - if (mg_strcmp(path, s_known_types[i]) == 0) return s_known_types[i + 1]; - } - - return mg_str("text/plain; charset=utf-8"); -} - -static int getrange(struct mg_str *s, size_t *a, size_t *b) { - size_t i, numparsed = 0; - for (i = 0; i + 6 < s->len; i++) { - struct mg_str k, v = mg_str_n(s->buf + i + 6, s->len - i - 6); - if (memcmp(&s->buf[i], "bytes=", 6) != 0) continue; - if (mg_span(v, &k, &v, '-')) { - if (mg_to_size_t(k, a)) numparsed++; - if (v.len > 0 && mg_to_size_t(v, b)) numparsed++; - } else { - if (mg_to_size_t(v, a)) numparsed++; - } - break; - } - return (int) numparsed; -} - -void mg_http_serve_file(struct mg_connection *c, struct mg_http_message *hm, - const char *path, - const struct mg_http_serve_opts *opts) { - char etag[64], tmp[MG_PATH_MAX]; - struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct mg_fd *fd = NULL; - size_t size = 0; - time_t mtime = 0; - struct mg_str *inm = NULL; - struct mg_str mime = guess_content_type(mg_str(path), opts->mime_types); - bool gzip = false; - - if (path != NULL) { - // If a browser sends us "Accept-Encoding: gzip", try to open .gz first - struct mg_str *ae = mg_http_get_header(hm, "Accept-Encoding"); - if (ae != NULL) { - char *ae_ = mg_mprintf("%.*s", ae->len, ae->buf); - if (ae_ != NULL && strstr(ae_, "gzip") != NULL) { - mg_snprintf(tmp, sizeof(tmp), "%s.gz", path); - fd = mg_fs_open(fs, tmp, MG_FS_READ); - if (fd != NULL) gzip = true, path = tmp; - } - free(ae_); - } - // No luck opening .gz? Open what we've told to open - if (fd == NULL) fd = mg_fs_open(fs, path, MG_FS_READ); - } - - // Failed to open, and page404 is configured? Open it, then - if (fd == NULL && opts->page404 != NULL) { - fd = mg_fs_open(fs, opts->page404, MG_FS_READ); - path = opts->page404; - mime = guess_content_type(mg_str(path), opts->mime_types); - } - - if (fd == NULL || fs->st(path, &size, &mtime) == 0) { - mg_http_reply(c, 404, opts->extra_headers, "Not found\n"); - mg_fs_close(fd); - // NOTE: mg_http_etag() call should go first! - } else if (mg_http_etag(etag, sizeof(etag), size, mtime) != NULL && - (inm = mg_http_get_header(hm, "If-None-Match")) != NULL && - mg_strcasecmp(*inm, mg_str(etag)) == 0) { - mg_fs_close(fd); - mg_http_reply(c, 304, opts->extra_headers, ""); - } else { - int n, status = 200; - char range[100]; - size_t r1 = 0, r2 = 0, cl = size; - - // Handle Range header - struct mg_str *rh = mg_http_get_header(hm, "Range"); - range[0] = '\0'; - if (rh != NULL && (n = getrange(rh, &r1, &r2)) > 0) { - // If range is specified like "400-", set second limit to content len - if (n == 1) r2 = cl - 1; - if (r1 > r2 || r2 >= cl) { - status = 416; - cl = 0; - mg_snprintf(range, sizeof(range), "Content-Range: bytes */%lld\r\n", - (int64_t) size); - } else { - status = 206; - cl = r2 - r1 + 1; - mg_snprintf(range, sizeof(range), - "Content-Range: bytes %llu-%llu/%llu\r\n", (uint64_t) r1, - (uint64_t) (r1 + cl - 1), (uint64_t) size); - fs->sk(fd->fd, r1); - } - } - mg_printf(c, - "HTTP/1.1 %d %s\r\n" - "Content-Type: %.*s\r\n" - "Etag: %s\r\n" - "Content-Length: %llu\r\n" - "%s%s%s\r\n", - status, mg_http_status_code_str(status), (int) mime.len, mime.buf, - etag, (uint64_t) cl, gzip ? "Content-Encoding: gzip\r\n" : "", - range, opts->extra_headers ? opts->extra_headers : ""); - if (mg_strcasecmp(hm->method, mg_str("HEAD")) == 0) { - c->is_resp = 0; - mg_fs_close(fd); - } else { - // Track to-be-sent content length at the end of c->data, aligned - size_t *clp = (size_t *) &c->data[(sizeof(c->data) - sizeof(size_t)) / - sizeof(size_t) * sizeof(size_t)]; - c->pfn = static_cb; - c->pfn_data = fd; - *clp = cl; - } - } -} - -struct printdirentrydata { - struct mg_connection *c; - struct mg_http_message *hm; - const struct mg_http_serve_opts *opts; - const char *dir; -}; - -#if MG_ENABLE_DIRLIST -static void printdirentry(const char *name, void *userdata) { - struct printdirentrydata *d = (struct printdirentrydata *) userdata; - struct mg_fs *fs = d->opts->fs == NULL ? &mg_fs_posix : d->opts->fs; - size_t size = 0; - time_t t = 0; - char path[MG_PATH_MAX], sz[40], mod[40]; - int flags, n = 0; - - // MG_DEBUG(("[%s] [%s]", d->dir, name)); - if (mg_snprintf(path, sizeof(path), "%s%c%s", d->dir, '/', name) > - sizeof(path)) { - MG_ERROR(("%s truncated", name)); - } else if ((flags = fs->st(path, &size, &t)) == 0) { - MG_ERROR(("%lu stat(%s): %d", d->c->id, path, errno)); - } else { - const char *slash = flags & MG_FS_DIR ? "/" : ""; - if (flags & MG_FS_DIR) { - mg_snprintf(sz, sizeof(sz), "%s", "[DIR]"); - } else { - mg_snprintf(sz, sizeof(sz), "%lld", (uint64_t) size); - } -#if defined(MG_HTTP_DIRLIST_TIME_FMT) - { - char time_str[40]; - struct tm *time_info = localtime(&t); - strftime(time_str, sizeof time_str, "%Y/%m/%d %H:%M:%S", time_info); - mg_snprintf(mod, sizeof(mod), "%s", time_str); - } -#else - mg_snprintf(mod, sizeof(mod), "%lu", (unsigned long) t); -#endif - n = (int) mg_url_encode(name, strlen(name), path, sizeof(path)); - mg_printf(d->c, - " %s%s" - "%s%s\n", - n, path, slash, name, slash, (unsigned long) t, mod, - flags & MG_FS_DIR ? (int64_t) -1 : (int64_t) size, sz); - } -} - -static void listdir(struct mg_connection *c, struct mg_http_message *hm, - const struct mg_http_serve_opts *opts, char *dir) { - const char *sort_js_code = - ""; - struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct printdirentrydata d = {c, hm, opts, dir}; - char tmp[10], buf[MG_PATH_MAX]; - size_t off, n; - int len = mg_url_decode(hm->uri.buf, hm->uri.len, buf, sizeof(buf), 0); - struct mg_str uri = len > 0 ? mg_str_n(buf, (size_t) len) : hm->uri; - - mg_printf(c, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html; charset=utf-8\r\n" - "%s" - "Content-Length: \r\n\r\n", - opts->extra_headers == NULL ? "" : opts->extra_headers); - off = c->send.len; // Start of body - mg_printf(c, - "Index of %.*s%s%s" - "" - "

Index of %.*s

" - "" - "" - "" - "" - "\n", - (int) uri.len, uri.buf, sort_js_code, sort_js_code2, (int) uri.len, - uri.buf); - mg_printf(c, "%s", - " " - "\n"); - - fs->ls(dir, printdirentry, &d); - mg_printf(c, - "" - "
Name" - "ModifiedSize

..[DIR]

Mongoose v.%s
\n", - MG_VERSION); - n = mg_snprintf(tmp, sizeof(tmp), "%lu", (unsigned long) (c->send.len - off)); - if (n > sizeof(tmp)) n = 0; - memcpy(c->send.buf + off - 12, tmp, n); // Set content length - c->is_resp = 0; // Mark response end -} -#endif - -// Resolve requested file into `path` and return its fs->st() result -static int uri_to_path2(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, struct mg_str url, struct mg_str dir, - char *path, size_t path_size) { - int flags, tmp; - // Append URI to the root_dir, and sanitize it - size_t n = mg_snprintf(path, path_size, "%.*s", (int) dir.len, dir.buf); - if (n + 2 >= path_size) { - mg_http_reply(c, 400, "", "Exceeded path size"); - return -1; - } - path[path_size - 1] = '\0'; - // Terminate root dir with slash - if (n > 0 && path[n - 1] != '/') path[n++] = '/', path[n] = '\0'; - if (url.len < hm->uri.len) { - mg_url_decode(hm->uri.buf + url.len, hm->uri.len - url.len, path + n, - path_size - n, 0); - } - path[path_size - 1] = '\0'; // Double-check - if (!mg_path_is_sane(mg_str_n(path, path_size))) { - mg_http_reply(c, 400, "", "Invalid path"); - return -1; - } - n = strlen(path); - while (n > 1 && path[n - 1] == '/') path[--n] = 0; // Trim trailing slashes - flags = mg_strcmp(hm->uri, mg_str("/")) == 0 ? MG_FS_DIR - : fs->st(path, NULL, NULL); - MG_VERBOSE(("%lu %.*s -> %s %d", c->id, (int) hm->uri.len, hm->uri.buf, path, - flags)); - if (flags == 0) { - // Do nothing - let's caller decide - } else if ((flags & MG_FS_DIR) && hm->uri.len > 0 && - hm->uri.buf[hm->uri.len - 1] != '/') { - mg_printf(c, - "HTTP/1.1 301 Moved\r\n" - "Location: %.*s/\r\n" - "Content-Length: 0\r\n" - "\r\n", - (int) hm->uri.len, hm->uri.buf); - c->is_resp = 0; - flags = -1; - } else if (flags & MG_FS_DIR) { - if (((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX) > 0 && - (tmp = fs->st(path, NULL, NULL)) != 0) || - (mg_snprintf(path + n, path_size - n, "/index.shtml") > 0 && - (tmp = fs->st(path, NULL, NULL)) != 0))) { - flags = tmp; - } else if ((mg_snprintf(path + n, path_size - n, "/" MG_HTTP_INDEX ".gz") > - 0 && - (tmp = fs->st(path, NULL, NULL)) != - 0)) { // check for gzipped index - flags = tmp; - path[n + 1 + strlen(MG_HTTP_INDEX)] = - '\0'; // Remove appended .gz in index file name - } else { - path[n] = '\0'; // Remove appended index file name - } - } - return flags; -} - -static int uri_to_path(struct mg_connection *c, struct mg_http_message *hm, - const struct mg_http_serve_opts *opts, char *path, - size_t path_size) { - struct mg_fs *fs = opts->fs == NULL ? &mg_fs_posix : opts->fs; - struct mg_str k, v, part, s = mg_str(opts->root_dir), u = {NULL, 0}, p = u; - while (mg_span(s, &part, &s, ',')) { - if (!mg_span(part, &k, &v, '=')) k = part, v = mg_str_n(NULL, 0); - if (v.len == 0) v = k, k = mg_str("/"), u = k, p = v; - if (hm->uri.len < k.len) continue; - if (mg_strcmp(k, mg_str_n(hm->uri.buf, k.len)) != 0) continue; - u = k, p = v; - } - return uri_to_path2(c, hm, fs, u, p, path, path_size); -} - -void mg_http_serve_dir(struct mg_connection *c, struct mg_http_message *hm, - const struct mg_http_serve_opts *opts) { - char path[MG_PATH_MAX]; - const char *sp = opts->ssi_pattern; - int flags = uri_to_path(c, hm, opts, path, sizeof(path)); - if (flags < 0) { - // Do nothing: the response has already been sent by uri_to_path() - } else if (flags & MG_FS_DIR) { -#if MG_ENABLE_DIRLIST - listdir(c, hm, opts, path); -#else - mg_http_reply(c, 403, "", "Forbidden\n"); -#endif - } else if (flags && sp != NULL && mg_match(mg_str(path), mg_str(sp), NULL)) { - mg_http_serve_ssi(c, opts->root_dir, path); - } else { - mg_http_serve_file(c, hm, path, opts); - } -} - -static bool mg_is_url_safe(int c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || c == '.' || c == '_' || c == '-' || c == '~'; -} - -size_t mg_url_encode(const char *s, size_t sl, char *buf, size_t len) { - size_t i, n = 0; - for (i = 0; i < sl; i++) { - int c = *(unsigned char *) &s[i]; - if (n + 4 >= len) return 0; - if (mg_is_url_safe(c)) { - buf[n++] = s[i]; - } else { - mg_snprintf(&buf[n], 4, "%%%M", mg_print_hex, 1, &s[i]); - n += 3; - } - } - if (len > 0 && n < len - 1) buf[n] = '\0'; // Null-terminate the destination - if (len > 0) buf[len - 1] = '\0'; // Always. - return n; -} - -void mg_http_creds(struct mg_http_message *hm, char *user, size_t userlen, - char *pass, size_t passlen) { - struct mg_str *v = mg_http_get_header(hm, "Authorization"); - user[0] = pass[0] = '\0'; - if (v != NULL && v->len > 6 && memcmp(v->buf, "Basic ", 6) == 0) { - char buf[256]; - size_t n = mg_base64_decode(v->buf + 6, v->len - 6, buf, sizeof(buf)); - const char *p = (const char *) memchr(buf, ':', n > 0 ? n : 0); - if (p != NULL) { - mg_snprintf(user, userlen, "%.*s", p - buf, buf); - mg_snprintf(pass, passlen, "%.*s", n - (size_t) (p - buf) - 1, p + 1); - } - } else if (v != NULL && v->len > 7 && memcmp(v->buf, "Bearer ", 7) == 0) { - mg_snprintf(pass, passlen, "%.*s", (int) v->len - 7, v->buf + 7); - } else if ((v = mg_http_get_header(hm, "Cookie")) != NULL) { - struct mg_str t = mg_http_get_header_var(*v, mg_str_n("access_token", 12)); - if (t.len > 0) mg_snprintf(pass, passlen, "%.*s", (int) t.len, t.buf); - } else { - mg_http_get_var(&hm->query, "access_token", pass, passlen); - } -} - -static struct mg_str stripquotes(struct mg_str s) { - return s.len > 1 && s.buf[0] == '"' && s.buf[s.len - 1] == '"' - ? mg_str_n(s.buf + 1, s.len - 2) - : s; -} - -struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) { - size_t i; - for (i = 0; v.len > 0 && i + v.len + 2 < s.len; i++) { - if (s.buf[i + v.len] == '=' && memcmp(&s.buf[i], v.buf, v.len) == 0) { - const char *p = &s.buf[i + v.len + 1], *b = p, *x = &s.buf[s.len]; - int q = p < x && *p == '"' ? 1 : 0; - while (p < x && - (q ? p == b || *p != '"' : *p != ';' && *p != ' ' && *p != ',')) - p++; - // MG_INFO(("[%.*s] [%.*s] [%.*s]", (int) s.len, s.buf, (int) v.len, - // v.buf, (int) (p - b), b)); - return stripquotes(mg_str_n(b, (size_t) (p - b + q))); - } - } - return mg_str_n(NULL, 0); -} - -long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm, - struct mg_fs *fs, const char *dir, size_t max_size) { - char buf[20] = "0", file[MG_PATH_MAX], path[MG_PATH_MAX]; - long res = 0, offset; - mg_http_get_var(&hm->query, "offset", buf, sizeof(buf)); - mg_http_get_var(&hm->query, "file", file, sizeof(file)); - offset = strtol(buf, NULL, 0); - mg_snprintf(path, sizeof(path), "%s%c%s", dir, MG_DIRSEP, file); - if (hm->body.len == 0) { - mg_http_reply(c, 200, "", "%ld", res); // Nothing to write - } else if (file[0] == '\0') { - mg_http_reply(c, 400, "", "file required"); - res = -1; - } else if (mg_path_is_sane(mg_str(file)) == false) { - mg_http_reply(c, 400, "", "%s: invalid file", file); - res = -2; - } else if (offset < 0) { - mg_http_reply(c, 400, "", "offset required"); - res = -3; - } else if ((size_t) offset + hm->body.len > max_size) { - mg_http_reply(c, 400, "", "%s: over max size of %lu", path, - (unsigned long) max_size); - res = -4; - } else { - struct mg_fd *fd; - size_t current_size = 0; - MG_DEBUG(("%s -> %lu bytes @ %ld", path, hm->body.len, offset)); - if (offset == 0) fs->rm(path); // If offset if 0, truncate file - fs->st(path, ¤t_size, NULL); - if (offset > 0 && current_size != (size_t) offset) { - mg_http_reply(c, 400, "", "%s: offset mismatch", path); - res = -5; - } else if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) { - mg_http_reply(c, 400, "", "open(%s): %d", path, errno); - res = -6; - } else { - res = offset + (long) fs->wr(fd->fd, hm->body.buf, hm->body.len); - mg_fs_close(fd); - mg_http_reply(c, 200, "", "%ld", res); - } - } - return res; -} - -int mg_http_status(const struct mg_http_message *hm) { - return atoi(hm->uri.buf); -} - -static bool is_hex_digit(int c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -static int skip_chunk(const char *buf, int len, int *pl, int *dl) { - int i = 0, n = 0; - if (len < 3) return 0; - while (i < len && is_hex_digit(buf[i])) i++; - if (i == 0) return -1; // Error, no length specified - if (i > (int) sizeof(int) * 2) return -1; // Chunk length is too big - if (len < i + 1 || buf[i] != '\r' || buf[i + 1] != '\n') return -1; // Error - if (mg_str_to_num(mg_str_n(buf, (size_t) i), 16, &n, sizeof(int)) == false) - return -1; // Decode chunk length, overflow - if (n < 0) return -1; // Error. TODO(): some checks now redundant - if (n > len - i - 4) return 0; // Chunk not yet fully buffered - if (buf[i + n + 2] != '\r' || buf[i + n + 3] != '\n') return -1; // Error - *pl = i + 2, *dl = n; - return i + 2 + n + 2; -} - -static void http_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ || ev == MG_EV_CLOSE || - (ev == MG_EV_POLL && c->is_accepted && !c->is_draining && - c->recv.len > 0)) { // see #2796 - struct mg_http_message hm; - size_t ofs = 0; // Parsing offset - while (c->is_resp == 0 && ofs < c->recv.len) { - const char *buf = (char *) c->recv.buf + ofs; - int n = mg_http_parse(buf, c->recv.len - ofs, &hm); - struct mg_str *te; // Transfer - encoding header - bool is_chunked = false; - size_t old_len = c->recv.len; - if (n < 0) { - // We don't use mg_error() here, to avoid closing pipelined requests - // prematurely, see #2592 - MG_ERROR(("HTTP parse, %lu bytes", c->recv.len)); - c->is_draining = 1; - mg_hexdump(buf, c->recv.len - ofs > 16 ? 16 : c->recv.len - ofs); - c->recv.len = 0; - return; - } - if (n == 0) break; // Request is not buffered yet - mg_call(c, MG_EV_HTTP_HDRS, &hm); // Got all HTTP headers - if (c->recv.len != old_len) { - // User manipulated received data. Wash our hands - MG_DEBUG(("%lu detaching HTTP handler", c->id)); - c->pfn = NULL; - return; - } - if (ev == MG_EV_CLOSE) { // If client did not set Content-Length - hm.message.len = c->recv.len - ofs; // and closes now, deliver MSG - hm.body.len = hm.message.len - (size_t) (hm.body.buf - hm.message.buf); - } - if ((te = mg_http_get_header(&hm, "Transfer-Encoding")) != NULL) { - if (mg_strcasecmp(*te, mg_str("chunked")) == 0) { - is_chunked = true; - } else { - mg_error(c, "Invalid Transfer-Encoding"); // See #2460 - return; - } - } else if (mg_http_get_header(&hm, "Content-length") == NULL) { - // #2593: HTTP packets must contain either Transfer-Encoding or - // Content-length - bool is_response = mg_ncasecmp(hm.method.buf, "HTTP/", 5) == 0; - bool require_content_len = false; - if (!is_response && (mg_strcasecmp(hm.method, mg_str("POST")) == 0 || - mg_strcasecmp(hm.method, mg_str("PUT")) == 0)) { - // POST and PUT should include an entity body. Therefore, they should - // contain a Content-length header. Other requests can also contain a - // body, but their content has no defined semantics (RFC 7231) - require_content_len = true; - ofs += (size_t) n; // this request has been processed - } else if (is_response) { - // HTTP spec 7.2 Entity body: All other responses must include a body - // or Content-Length header field defined with a value of 0. - int status = mg_http_status(&hm); - require_content_len = status >= 200 && status != 204 && status != 304; - } - if (require_content_len) { - if (!c->is_client) mg_http_reply(c, 411, "", ""); - MG_ERROR(("Content length missing from %s", is_response ? "response" : "request")); - } - } - - if (is_chunked) { - // For chunked data, strip off prefixes and suffixes from chunks - // and relocate them right after the headers, then report a message - char *s = (char *) c->recv.buf + ofs + n; - int o = 0, pl, dl, cl, len = (int) (c->recv.len - ofs - (size_t) n); - - // Find zero-length chunk (the end of the body) - while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0 && dl) o += cl; - if (cl == 0) break; // No zero-len chunk, buffer more data - if (cl < 0) { - mg_error(c, "Invalid chunk"); - break; - } - - // Zero chunk found. Second pass: strip + relocate - o = 0, hm.body.len = 0, hm.message.len = (size_t) n; - while ((cl = skip_chunk(s + o, len - o, &pl, &dl)) > 0) { - memmove(s + hm.body.len, s + o + pl, (size_t) dl); - o += cl, hm.body.len += (size_t) dl, hm.message.len += (size_t) dl; - if (dl == 0) break; - } - ofs += (size_t) (n + o); - } else { // Normal, non-chunked data - size_t len = c->recv.len - ofs - (size_t) n; - if (hm.body.len > len) break; // Buffer more data - ofs += (size_t) n + hm.body.len; - } - - if (c->is_accepted) c->is_resp = 1; // Start generating response - mg_call(c, MG_EV_HTTP_MSG, &hm); // User handler can clear is_resp - if (c->is_accepted && !c->is_resp) { - struct mg_str *cc = mg_http_get_header(&hm, "Connection"); - if (cc != NULL && mg_strcasecmp(*cc, mg_str("close")) == 0) { - c->is_draining = 1; // honor "Connection: close" - break; - } - } - } - if (ofs > 0) mg_iobuf_del(&c->recv, 0, ofs); // Delete processed data - } - (void) ev_data; -} - -static void mg_hfn(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - if (mg_match(hm->uri, mg_str("/quit"), NULL)) { - mg_http_reply(c, 200, "", "ok\n"); - c->is_draining = 1; - c->data[0] = 'X'; - } else if (mg_match(hm->uri, mg_str("/debug"), NULL)) { - int level = (int) mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG); - mg_log_set(level); - mg_http_reply(c, 200, "", "Debug level set to %d\n", level); - } else { - mg_http_reply(c, 200, "", "hi\n"); - } - } else if (ev == MG_EV_CLOSE) { - if (c->data[0] == 'X') *(bool *) c->fn_data = true; - } -} - -void mg_hello(const char *url) { - struct mg_mgr mgr; - bool done = false; - mg_mgr_init(&mgr); - if (mg_http_listen(&mgr, url, mg_hfn, &done) == NULL) done = true; - while (done == false) mg_mgr_poll(&mgr, 100); - mg_mgr_free(&mgr); -} - -struct mg_connection *mg_http_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); - if (c != NULL) c->pfn = http_cb; - return c; -} - -struct mg_connection *mg_http_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); - if (c != NULL) c->pfn = http_cb; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/iobuf.c" -#endif - - - - - -static size_t roundup(size_t size, size_t align) { - return align == 0 ? size : (size + align - 1) / align * align; -} - -int mg_iobuf_resize(struct mg_iobuf *io, size_t new_size) { - int ok = 1; - new_size = roundup(new_size, io->align); - if (new_size == 0) { - mg_bzero(io->buf, io->size); - free(io->buf); - io->buf = NULL; - io->len = io->size = 0; - } else if (new_size != io->size) { - // NOTE(lsm): do not use realloc here. Use calloc/free only, to ease the - // porting to some obscure platforms like FreeRTOS - void *p = calloc(1, new_size); - if (p != NULL) { - size_t len = new_size < io->len ? new_size : io->len; - if (len > 0 && io->buf != NULL) memmove(p, io->buf, len); - mg_bzero(io->buf, io->size); - free(io->buf); - io->buf = (unsigned char *) p; - io->size = new_size; - } else { - ok = 0; - MG_ERROR(("%lld->%lld", (uint64_t) io->size, (uint64_t) new_size)); - } - } - return ok; -} - -int mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align) { - io->buf = NULL; - io->align = align; - io->size = io->len = 0; - return mg_iobuf_resize(io, size); -} - -size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf, - size_t len) { - size_t new_size = roundup(io->len + len, io->align); - mg_iobuf_resize(io, new_size); // Attempt to resize - if (new_size != io->size) len = 0; // Resize failure, append nothing - if (ofs < io->len) memmove(io->buf + ofs + len, io->buf + ofs, io->len - ofs); - if (buf != NULL) memmove(io->buf + ofs, buf, len); - if (ofs > io->len) io->len += ofs - io->len; - io->len += len; - return len; -} - -size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len) { - if (ofs > io->len) ofs = io->len; - if (ofs + len > io->len) len = io->len - ofs; - if (io->buf) memmove(io->buf + ofs, io->buf + ofs + len, io->len - ofs - len); - if (io->buf) mg_bzero(io->buf + io->len - len, len); - io->len -= len; - return len; -} - -void mg_iobuf_free(struct mg_iobuf *io) { - mg_iobuf_resize(io, 0); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/json.c" -#endif - - - - -static const char *escapeseq(int esc) { - return esc ? "\b\f\n\r\t\\\"" : "bfnrt\\\""; -} - -static char json_esc(int c, int esc) { - const char *p, *esc1 = escapeseq(esc), *esc2 = escapeseq(!esc); - for (p = esc1; *p != '\0'; p++) { - if (*p == c) return esc2[p - esc1]; - } - return 0; -} - -static int mg_pass_string(const char *s, int len) { - int i; - for (i = 0; i < len; i++) { - if (s[i] == '\\' && i + 1 < len && json_esc(s[i + 1], 1)) { - i++; - } else if (s[i] == '\0') { - return MG_JSON_INVALID; - } else if (s[i] == '"') { - return i; - } - } - return MG_JSON_INVALID; -} - -static double mg_atod(const char *p, int len, int *numlen) { - double d = 0.0; - int i = 0, sign = 1; - - // Sign - if (i < len && *p == '-') { - sign = -1, i++; - } else if (i < len && *p == '+') { - i++; - } - - // Decimal - for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { - d *= 10.0; - d += p[i] - '0'; - } - d *= sign; - - // Fractional - if (i < len && p[i] == '.') { - double frac = 0.0, base = 0.1; - i++; - for (; i < len && p[i] >= '0' && p[i] <= '9'; i++) { - frac += base * (p[i] - '0'); - base /= 10.0; - } - d += frac * sign; - } - - // Exponential - if (i < len && (p[i] == 'e' || p[i] == 'E')) { - int j, exp = 0, minus = 0; - i++; - if (i < len && p[i] == '-') minus = 1, i++; - if (i < len && p[i] == '+') i++; - while (i < len && p[i] >= '0' && p[i] <= '9' && exp < 308) - exp = exp * 10 + (p[i++] - '0'); - if (minus) exp = -exp; - for (j = 0; j < exp; j++) d *= 10.0; - for (j = 0; j < -exp; j++) d /= 10.0; - } - - if (numlen != NULL) *numlen = i; - return d; -} - -// Iterate over object or array elements -size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key, - struct mg_str *val) { - if (ofs >= obj.len) { - ofs = 0; // Out of boundaries, stop scanning - } else if (obj.len < 2 || (*obj.buf != '{' && *obj.buf != '[')) { - ofs = 0; // Not an array or object, stop - } else { - struct mg_str sub = mg_str_n(obj.buf + ofs, obj.len - ofs); - if (ofs == 0) ofs++, sub.buf++, sub.len--; - if (*obj.buf == '[') { // Iterate over an array - int n = 0, o = mg_json_get(sub, "$", &n); - if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) { - ofs = 0; // Error parsing key, stop scanning - } else { - if (key) *key = mg_str_n(NULL, 0); - if (val) *val = mg_str_n(sub.buf + o, (size_t) n); - ofs = (size_t) (&sub.buf[o + n] - obj.buf); - } - } else { // Iterate over an object - int n = 0, o = mg_json_get(sub, "$", &n); - if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) { - ofs = 0; // Error parsing key, stop scanning - } else { - if (key) *key = mg_str_n(sub.buf + o, (size_t) n); - sub.buf += o + n, sub.len -= (size_t) (o + n); - while (sub.len > 0 && *sub.buf != ':') sub.len--, sub.buf++; - if (sub.len > 0 && *sub.buf == ':') sub.len--, sub.buf++; - n = 0, o = mg_json_get(sub, "$", &n); - if (n < 0 || o < 0 || (size_t) (o + n) > sub.len) { - ofs = 0; // Error parsing value, stop scanning - } else { - if (val) *val = mg_str_n(sub.buf + o, (size_t) n); - ofs = (size_t) (&sub.buf[o + n] - obj.buf); - } - } - } - // MG_INFO(("SUB ofs %u %.*s", ofs, sub.len, sub.buf)); - while (ofs && ofs < obj.len && - (obj.buf[ofs] == ' ' || obj.buf[ofs] == '\t' || - obj.buf[ofs] == '\n' || obj.buf[ofs] == '\r')) { - ofs++; - } - if (ofs && ofs < obj.len && obj.buf[ofs] == ',') ofs++; - if (ofs > obj.len) ofs = 0; - } - return ofs; -} - -int mg_json_get(struct mg_str json, const char *path, int *toklen) { - const char *s = json.buf; - int len = (int) json.len; - enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE; - unsigned char nesting[MG_JSON_MAX_DEPTH]; - int i = 0; // Current offset in `s` - int j = 0; // Offset in `s` we're looking for (return value) - int depth = 0; // Current depth (nesting level) - int ed = 0; // Expected depth - int pos = 1; // Current position in `path` - int ci = -1, ei = -1; // Current and expected index in array - - if (toklen) *toklen = 0; - if (path[0] != '$') return MG_JSON_INVALID; - -#define MG_CHECKRET(x) \ - do { \ - if (depth == ed && path[pos] == '\0' && ci == ei) { \ - if (toklen) *toklen = i - j + 1; \ - return j; \ - } \ - } while (0) - -// In the ascii table, the distance between `[` and `]` is 2. -// Ditto for `{` and `}`. Hence +2 in the code below. -#define MG_EOO(x) \ - do { \ - if (depth == ed && ci != ei) return MG_JSON_NOT_FOUND; \ - if (c != nesting[depth - 1] + 2) return MG_JSON_INVALID; \ - depth--; \ - MG_CHECKRET(x); \ - } while (0) - - for (i = 0; i < len; i++) { - unsigned char c = ((unsigned char *) s)[i]; - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; - switch (expecting) { - case S_VALUE: - // p("V %s [%.*s] %d %d %d %d\n", path, pos, path, depth, ed, ci, ei); - if (depth == ed) j = i; - if (c == '{') { - if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP; - if (depth == ed && path[pos] == '.' && ci == ei) { - // If we start the object, reset array indices - ed++, pos++, ci = ei = -1; - } - nesting[depth++] = c; - expecting = S_KEY; - break; - } else if (c == '[') { - if (depth >= (int) sizeof(nesting)) return MG_JSON_TOO_DEEP; - if (depth == ed && path[pos] == '[' && ei == ci) { - ed++, pos++, ci = 0; - for (ei = 0; path[pos] != ']' && path[pos] != '\0'; pos++) { - ei *= 10; - ei += path[pos] - '0'; - } - if (path[pos] != 0) pos++; - } - nesting[depth++] = c; - break; - } else if (c == ']' && depth > 0) { // Empty array - MG_EOO(']'); - } else if (c == 't' && i + 3 < len && memcmp(&s[i], "true", 4) == 0) { - i += 3; - } else if (c == 'n' && i + 3 < len && memcmp(&s[i], "null", 4) == 0) { - i += 3; - } else if (c == 'f' && i + 4 < len && memcmp(&s[i], "false", 5) == 0) { - i += 4; - } else if (c == '-' || ((c >= '0' && c <= '9'))) { - int numlen = 0; - mg_atod(&s[i], len - i, &numlen); - i += numlen - 1; - } else if (c == '"') { - int n = mg_pass_string(&s[i + 1], len - i - 1); - if (n < 0) return n; - i += n + 1; - } else { - return MG_JSON_INVALID; - } - MG_CHECKRET('V'); - if (depth == ed && ei >= 0) ci++; - expecting = S_COMMA_OR_EOO; - break; - - case S_KEY: - if (c == '"') { - int n = mg_pass_string(&s[i + 1], len - i - 1); - if (n < 0) return n; - if (i + 1 + n >= len) return MG_JSON_NOT_FOUND; - if (depth < ed) return MG_JSON_NOT_FOUND; - if (depth == ed && path[pos - 1] != '.') return MG_JSON_NOT_FOUND; - // printf("K %s [%.*s] [%.*s] %d %d %d %d %d\n", path, pos, path, n, - // &s[i + 1], n, depth, ed, ci, ei); - // NOTE(cpq): in the check sequence below is important. - // strncmp() must go first: it fails fast if the remaining length - // of the path is smaller than `n`. - if (depth == ed && path[pos - 1] == '.' && - strncmp(&s[i + 1], &path[pos], (size_t) n) == 0 && - (path[pos + n] == '\0' || path[pos + n] == '.' || - path[pos + n] == '[')) { - pos += n; - } - i += n + 1; - expecting = S_COLON; - } else if (c == '}') { // Empty object - MG_EOO('}'); - expecting = S_COMMA_OR_EOO; - if (depth == ed && ei >= 0) ci++; - } else { - return MG_JSON_INVALID; - } - break; - - case S_COLON: - if (c == ':') { - expecting = S_VALUE; - } else { - return MG_JSON_INVALID; - } - break; - - case S_COMMA_OR_EOO: - if (depth <= 0) { - return MG_JSON_INVALID; - } else if (c == ',') { - expecting = (nesting[depth - 1] == '{') ? S_KEY : S_VALUE; - } else if (c == ']' || c == '}') { - if (depth == ed && c == '}' && path[pos - 1] == '.') - return MG_JSON_NOT_FOUND; - if (depth == ed && c == ']' && path[pos - 1] == ',') - return MG_JSON_NOT_FOUND; - MG_EOO('O'); - if (depth == ed && ei >= 0) ci++; - } else { - return MG_JSON_INVALID; - } - break; - } - } - return MG_JSON_NOT_FOUND; -} - -struct mg_str mg_json_get_tok(struct mg_str json, const char *path) { - int len = 0, ofs = mg_json_get(json, path, &len); - return mg_str_n(ofs < 0 ? NULL : json.buf + ofs, - (size_t) (len < 0 ? 0 : len)); -} - -bool mg_json_get_num(struct mg_str json, const char *path, double *v) { - int n, toklen, found = 0; - if ((n = mg_json_get(json, path, &toklen)) >= 0 && - (json.buf[n] == '-' || (json.buf[n] >= '0' && json.buf[n] <= '9'))) { - if (v != NULL) *v = mg_atod(json.buf + n, toklen, NULL); - found = 1; - } - return found; -} - -bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) { - int found = 0, off = mg_json_get(json, path, NULL); - if (off >= 0 && (json.buf[off] == 't' || json.buf[off] == 'f')) { - if (v != NULL) *v = json.buf[off] == 't'; - found = 1; - } - return found; -} - -bool mg_json_unescape(struct mg_str s, char *to, size_t n) { - size_t i, j; - for (i = 0, j = 0; i < s.len && j < n; i++, j++) { - if (s.buf[i] == '\\' && i + 5 < s.len && s.buf[i + 1] == 'u') { - // \uXXXX escape. We process simple one-byte chars \u00xx within ASCII - // range. More complex chars would require dragging in a UTF8 library, - // which is too much for us - if (mg_str_to_num(mg_str_n(s.buf + i + 2, 4), 16, &to[j], - sizeof(uint8_t)) == false) - return false; - i += 5; - } else if (s.buf[i] == '\\' && i + 1 < s.len) { - char c = json_esc(s.buf[i + 1], 0); - if (c == 0) return false; - to[j] = c; - i++; - } else { - to[j] = s.buf[i]; - } - } - if (j >= n) return false; - if (n > 0) to[j] = '\0'; - return true; -} - -char *mg_json_get_str(struct mg_str json, const char *path) { - char *result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && len > 1 && json.buf[off] == '"') { - if ((result = (char *) calloc(1, (size_t) len)) != NULL && - !mg_json_unescape(mg_str_n(json.buf + off + 1, (size_t) (len - 2)), - result, (size_t) len)) { - free(result); - result = NULL; - } - } - return result; -} - -char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) { - char *result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && json.buf[off] == '"' && len > 1 && - (result = (char *) calloc(1, (size_t) len)) != NULL) { - size_t k = mg_base64_decode(json.buf + off + 1, (size_t) (len - 2), result, - (size_t) len); - if (slen != NULL) *slen = (int) k; - } - return result; -} - -char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) { - char *result = NULL; - int len = 0, off = mg_json_get(json, path, &len); - if (off >= 0 && json.buf[off] == '"' && len > 1 && - (result = (char *) calloc(1, (size_t) len / 2)) != NULL) { - int i; - for (i = 0; i < len - 2; i += 2) { - mg_str_to_num(mg_str_n(json.buf + off + 1 + i, 2), 16, &result[i >> 1], - sizeof(uint8_t)); - } - result[len / 2 - 1] = '\0'; - if (slen != NULL) *slen = len / 2 - 1; - } - return result; -} - -long mg_json_get_long(struct mg_str json, const char *path, long dflt) { - double dv; - long result = dflt; - if (mg_json_get_num(json, path, &dv)) result = (long) dv; - return result; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/log.c" -#endif - - - - - -int mg_log_level = MG_LL_INFO; -static mg_pfn_t s_log_func = mg_pfn_stdout; -static void *s_log_func_param = NULL; - -void mg_log_set_fn(mg_pfn_t fn, void *param) { - s_log_func = fn; - s_log_func_param = param; -} - -static void logc(unsigned char c) { - s_log_func((char) c, s_log_func_param); -} - -static void logs(const char *buf, size_t len) { - size_t i; - for (i = 0; i < len; i++) logc(((unsigned char *) buf)[i]); -} - -#if MG_ENABLE_CUSTOM_LOG -// Let user define their own mg_log_prefix() and mg_log() -#else -void mg_log_prefix(int level, const char *file, int line, const char *fname) { - const char *p = strrchr(file, '/'); - char buf[41]; - size_t n; - if (p == NULL) p = strrchr(file, '\\'); - n = mg_snprintf(buf, sizeof(buf), "%-6llx %d %s:%d:%s", mg_millis(), level, - p == NULL ? file : p + 1, line, fname); - if (n > sizeof(buf) - 2) n = sizeof(buf) - 2; - while (n < sizeof(buf)) buf[n++] = ' '; - logs(buf, n - 1); -} - -void mg_log(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_vxprintf(s_log_func, s_log_func_param, fmt, &ap); - va_end(ap); - logs("\r\n", 2); -} -#endif - -static unsigned char nibble(unsigned c) { - return (unsigned char) (c < 10 ? c + '0' : c + 'W'); -} - -#define ISPRINT(x) ((x) >= ' ' && (x) <= '~') -void mg_hexdump(const void *buf, size_t len) { - const unsigned char *p = (const unsigned char *) buf; - unsigned char ascii[16], alen = 0; - size_t i; - for (i = 0; i < len; i++) { - if ((i % 16) == 0) { - // Print buffered ascii chars - if (i > 0) logs(" ", 2), logs((char *) ascii, 16), logc('\n'), alen = 0; - // Print hex address, then \t - logc(nibble((i >> 12) & 15)), logc(nibble((i >> 8) & 15)), - logc(nibble((i >> 4) & 15)), logc('0'), logs(" ", 3); - } - logc(nibble(p[i] >> 4)), logc(nibble(p[i] & 15)); // Two nibbles, e.g. c5 - logc(' '); // Space after hex number - ascii[alen++] = ISPRINT(p[i]) ? p[i] : '.'; // Add to the ascii buf - } - while (alen < 16) logs(" ", 3), ascii[alen++] = ' '; - logs(" ", 2), logs((char *) ascii, 16), logc('\n'); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/md5.c" -#endif - - - -// This code implements the MD5 message-digest algorithm. -// The algorithm is due to Ron Rivest. This code was -// written by Colin Plumb in 1993, no copyright is claimed. -// This code is in the public domain; do with it what you wish. -// -// Equivalent code is available from RSA Data Security, Inc. -// This code has been tested against that, and is equivalent, -// except that you don't need to include two pages of legalese -// with every copy. -// -// To compute the message digest of a chunk of bytes, declare an -// MD5Context structure, pass it to MD5Init, call MD5Update as -// needed on buffers full of bytes, and then call MD5Final, which -// will fill a supplied 16-byte array with the digest. - -#if defined(MG_ENABLE_MD5) && MG_ENABLE_MD5 - -static void mg_byte_reverse(unsigned char *buf, unsigned longs) { - if (MG_BIG_ENDIAN) { - do { - uint32_t t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(uint32_t *) buf = t; - buf += 4; - } while (--longs); - } else { - (void) buf, (void) longs; // Little endian. Do nothing - } -} - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void mg_md5_init(mg_md5_ctx *ctx) { - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -static void mg_md5_transform(uint32_t buf[4], uint32_t const in[16]) { - uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -void mg_md5_update(mg_md5_ctx *ctx, const unsigned char *buf, size_t len) { - uint32_t t; - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; - ctx->bits[1] += (uint32_t) len >> 29; - - t = (t >> 3) & 0x3f; - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - memcpy(ctx->in, buf, len); -} - -void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) { - unsigned count; - unsigned char *p; - uint32_t *a; - - count = (ctx->bits[0] >> 3) & 0x3F; - - p = ctx->in + count; - *p++ = 0x80; - count = 64 - 1 - count; - if (count < 8) { - memset(p, 0, count); - mg_byte_reverse(ctx->in, 16); - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - memset(ctx->in, 0, 56); - } else { - memset(p, 0, count - 8); - } - mg_byte_reverse(ctx->in, 14); - - a = (uint32_t *) ctx->in; - a[14] = ctx->bits[0]; - a[15] = ctx->bits[1]; - - mg_md5_transform(ctx->buf, (uint32_t *) ctx->in); - mg_byte_reverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset((char *) ctx, 0, sizeof(*ctx)); -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/mqtt.c" -#endif - - - - - - - - -#define MQTT_CLEAN_SESSION 0x02 -#define MQTT_HAS_WILL 0x04 -#define MQTT_WILL_RETAIN 0x20 -#define MQTT_HAS_PASSWORD 0x40 -#define MQTT_HAS_USER_NAME 0x80 - -struct mg_mqtt_pmap { - uint8_t id; - uint8_t type; -}; - -static const struct mg_mqtt_pmap s_prop_map[] = { - {MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY_DATA}, - {MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARIABLE_INT}, - {MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY_DATA}, - {MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING}, - {MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_SHORT}, - {MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR}, - {MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT}, - {MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE, MQTT_PROP_TYPE_BYTE}, - {MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE, MQTT_PROP_TYPE_BYTE}}; - -void mg_mqtt_send_header(struct mg_connection *c, uint8_t cmd, uint8_t flags, - uint32_t len) { - uint8_t buf[1 + sizeof(len)], *vlen = &buf[1]; - buf[0] = (uint8_t) ((cmd << 4) | flags); - do { - *vlen = len % 0x80; - len /= 0x80; - if (len > 0) *vlen |= 0x80; - vlen++; - } while (len > 0 && vlen < &buf[sizeof(buf)]); - mg_send(c, buf, (size_t) (vlen - buf)); -} - -static void mg_send_u16(struct mg_connection *c, uint16_t value) { - mg_send(c, &value, sizeof(value)); -} - -static void mg_send_u32(struct mg_connection *c, uint32_t value) { - mg_send(c, &value, sizeof(value)); -} - -static uint8_t varint_size(size_t length) { - uint8_t bytes_needed = 0; - do { - bytes_needed++; - length /= 0x80; - } while (length > 0); - return bytes_needed; -} - -static size_t encode_varint(uint8_t *buf, size_t value) { - size_t len = 0; - - do { - uint8_t b = (uint8_t) (value % 128); - value /= 128; - if (value > 0) b |= 0x80; - buf[len++] = b; - } while (value > 0); - - return len; -} - -static size_t decode_varint(const uint8_t *buf, size_t len, size_t *value) { - size_t multiplier = 1, offset; - *value = 0; - - for (offset = 0; offset < 4 && offset < len; offset++) { - uint8_t encoded_byte = buf[offset]; - *value += (encoded_byte & 0x7f) * multiplier; - multiplier *= 128; - - if ((encoded_byte & 0x80) == 0) return offset + 1; - } - - return 0; -} - -static int mqtt_prop_type_by_id(uint8_t prop_id) { - size_t i, num_properties = sizeof(s_prop_map) / sizeof(s_prop_map[0]); - for (i = 0; i < num_properties; ++i) { - if (s_prop_map[i].id == prop_id) return s_prop_map[i].type; - } - return -1; // Property ID not found -} - -// Returns the size of the properties section, without the -// size of the content's length -static size_t get_properties_length(struct mg_mqtt_prop *props, size_t count) { - size_t i, size = 0; - for (i = 0; i < count; i++) { - size++; // identifier - switch (mqtt_prop_type_by_id(props[i].id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - size += (uint32_t) (props[i].val.len + props[i].key.len + - 2 * sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_STRING: - size += (uint32_t) (props[i].val.len + sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_BINARY_DATA: - size += (uint32_t) (props[i].val.len + sizeof(uint16_t)); - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - size += varint_size((uint32_t) props[i].iv); - break; - case MQTT_PROP_TYPE_INT: - size += (uint32_t) sizeof(uint32_t); - break; - case MQTT_PROP_TYPE_SHORT: - size += (uint32_t) sizeof(uint16_t); - break; - case MQTT_PROP_TYPE_BYTE: - size += (uint32_t) sizeof(uint8_t); - break; - default: - return size; // cannot parse further down - } - } - - return size; -} - -// returns the entire size of the properties section, including the -// size of the variable length of the content -static size_t get_props_size(struct mg_mqtt_prop *props, size_t count) { - size_t size = get_properties_length(props, count); - size += varint_size(size); - return size; -} - -static void mg_send_mqtt_properties(struct mg_connection *c, - struct mg_mqtt_prop *props, size_t nprops) { - size_t total_size = get_properties_length(props, nprops); - uint8_t buf_v[4] = {0, 0, 0, 0}; - uint8_t buf[4] = {0, 0, 0, 0}; - size_t i, len = encode_varint(buf, total_size); - - mg_send(c, buf, (size_t) len); - for (i = 0; i < nprops; i++) { - mg_send(c, &props[i].id, sizeof(props[i].id)); - switch (mqtt_prop_type_by_id(props[i].id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - mg_send_u16(c, mg_htons((uint16_t) props[i].key.len)); - mg_send(c, props[i].key.buf, props[i].key.len); - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); - break; - case MQTT_PROP_TYPE_BYTE: - mg_send(c, &props[i].iv, sizeof(uint8_t)); - break; - case MQTT_PROP_TYPE_SHORT: - mg_send_u16(c, mg_htons((uint16_t) props[i].iv)); - break; - case MQTT_PROP_TYPE_INT: - mg_send_u32(c, mg_htonl((uint32_t) props[i].iv)); - break; - case MQTT_PROP_TYPE_STRING: - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); - break; - case MQTT_PROP_TYPE_BINARY_DATA: - mg_send_u16(c, mg_htons((uint16_t) props[i].val.len)); - mg_send(c, props[i].val.buf, props[i].val.len); - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - len = encode_varint(buf_v, props[i].iv); - mg_send(c, buf_v, (size_t) len); - break; - } - } -} - -size_t mg_mqtt_next_prop(struct mg_mqtt_message *msg, struct mg_mqtt_prop *prop, - size_t ofs) { - uint8_t *i = (uint8_t *) msg->dgram.buf + msg->props_start + ofs; - uint8_t *end = (uint8_t *) msg->dgram.buf + msg->dgram.len; - size_t new_pos = ofs, len; - prop->id = i[0]; - - if (ofs >= msg->dgram.len || ofs >= msg->props_start + msg->props_size) - return 0; - i++, new_pos++; - - switch (mqtt_prop_type_by_id(prop->id)) { - case MQTT_PROP_TYPE_STRING_PAIR: - prop->key.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->key.buf = (char *) i + 2; - i += 2 + prop->key.len; - prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->val.buf = (char *) i + 2; - new_pos += 2 * sizeof(uint16_t) + prop->val.len + prop->key.len; - break; - case MQTT_PROP_TYPE_BYTE: - prop->iv = (uint8_t) i[0]; - new_pos++; - break; - case MQTT_PROP_TYPE_SHORT: - prop->iv = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - new_pos += sizeof(uint16_t); - break; - case MQTT_PROP_TYPE_INT: - prop->iv = ((uint32_t) i[0] << 24) | ((uint32_t) i[1] << 16) | - ((uint32_t) i[2] << 8) | i[3]; - new_pos += sizeof(uint32_t); - break; - case MQTT_PROP_TYPE_STRING: - prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->val.buf = (char *) i + 2; - new_pos += 2 + prop->val.len; - break; - case MQTT_PROP_TYPE_BINARY_DATA: - prop->val.len = (uint16_t) ((((uint16_t) i[0]) << 8) | i[1]); - prop->val.buf = (char *) i + 2; - new_pos += 2 + prop->val.len; - break; - case MQTT_PROP_TYPE_VARIABLE_INT: - len = decode_varint(i, (size_t) (end - i), (size_t *) &prop->iv); - new_pos = (!len) ? 0 : new_pos + len; - break; - default: - new_pos = 0; - } - - return new_pos; -} - -void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts) { - char client_id[21]; - struct mg_str cid = opts->client_id; - size_t total_len = 7 + 1 + 2 + 2; - uint8_t hdr[8] = {0, 4, 'M', 'Q', 'T', 'T', opts->version, 0}; - - if (cid.len == 0) { - mg_random_str(client_id, sizeof(client_id) - 1); - client_id[sizeof(client_id) - 1] = '\0'; - cid = mg_str(client_id); - } - - if (hdr[6] == 0) hdr[6] = 4; // If version is not set, use 4 (3.1.1) - c->is_mqtt5 = hdr[6] == 5; // Set version 5 flag - hdr[7] = (uint8_t) ((opts->qos & 3) << 3); // Connection flags - if (opts->user.len > 0) { - total_len += 2 + (uint32_t) opts->user.len; - hdr[7] |= MQTT_HAS_USER_NAME; - } - if (opts->pass.len > 0) { - total_len += 2 + (uint32_t) opts->pass.len; - hdr[7] |= MQTT_HAS_PASSWORD; - } - if (opts->topic.len > 0) { // allow zero-length msgs, message.len is size_t - total_len += 4 + (uint32_t) opts->topic.len + (uint32_t) opts->message.len; - hdr[7] |= MQTT_HAS_WILL; - } - if (opts->clean || cid.len == 0) hdr[7] |= MQTT_CLEAN_SESSION; - if (opts->retain) hdr[7] |= MQTT_WILL_RETAIN; - total_len += (uint32_t) cid.len; - if (c->is_mqtt5) { - total_len += get_props_size(opts->props, opts->num_props); - if (hdr[7] & MQTT_HAS_WILL) - total_len += get_props_size(opts->will_props, opts->num_will_props); - } - - mg_mqtt_send_header(c, MQTT_CMD_CONNECT, 0, (uint32_t) total_len); - mg_send(c, hdr, sizeof(hdr)); - // keepalive == 0 means "do not disconnect us!" - mg_send_u16(c, mg_htons((uint16_t) opts->keepalive)); - - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send_u16(c, mg_htons((uint16_t) cid.len)); - mg_send(c, cid.buf, cid.len); - - if (hdr[7] & MQTT_HAS_WILL) { - if (c->is_mqtt5) - mg_send_mqtt_properties(c, opts->will_props, opts->num_will_props); - - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - mg_send_u16(c, mg_htons((uint16_t) opts->message.len)); - mg_send(c, opts->message.buf, opts->message.len); - } - if (opts->user.len > 0) { - mg_send_u16(c, mg_htons((uint16_t) opts->user.len)); - mg_send(c, opts->user.buf, opts->user.len); - } - if (opts->pass.len > 0) { - mg_send_u16(c, mg_htons((uint16_t) opts->pass.len)); - mg_send(c, opts->pass.buf, opts->pass.len); - } -} - -uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { - uint16_t id = opts->retransmit_id; - uint8_t flags = (uint8_t) (((opts->qos & 3) << 1) | (opts->retain ? 1 : 0)); - size_t len = 2 + opts->topic.len + opts->message.len; - MG_DEBUG(("%lu [%.*s] <- [%.*s%c", c->id, (int) opts->topic.len, - (char *) opts->topic.buf, - (int) (opts->message.len <= 10 ? opts->message.len : 10), - (char *) opts->message.buf, opts->message.len <= 10 ? ']' : ' ')); - if (opts->qos > 0) len += 2; - if (c->is_mqtt5) len += get_props_size(opts->props, opts->num_props); - - if (opts->qos > 0 && id != 0) flags |= 1 << 3; - mg_mqtt_send_header(c, MQTT_CMD_PUBLISH, flags, (uint32_t) len); - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - if (opts->qos > 0) { // need to send 'id' field - if (id == 0) { // generate new one if not resending - if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; - id = c->mgr->mqtt_id; - } - mg_send_u16(c, mg_htons(id)); - } - - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); - - if (opts->message.len > 0) mg_send(c, opts->message.buf, opts->message.len); - return id; -} - -void mg_mqtt_sub(struct mg_connection *c, const struct mg_mqtt_opts *opts) { - uint8_t qos_ = opts->qos & 3; - size_t plen = c->is_mqtt5 ? get_props_size(opts->props, opts->num_props) : 0; - size_t len = 2 + opts->topic.len + 2 + 1 + plen; - - mg_mqtt_send_header(c, MQTT_CMD_SUBSCRIBE, 2, (uint32_t) len); - if (++c->mgr->mqtt_id == 0) ++c->mgr->mqtt_id; - mg_send_u16(c, mg_htons(c->mgr->mqtt_id)); - if (c->is_mqtt5) mg_send_mqtt_properties(c, opts->props, opts->num_props); - - mg_send_u16(c, mg_htons((uint16_t) opts->topic.len)); - mg_send(c, opts->topic.buf, opts->topic.len); - mg_send(c, &qos_, sizeof(qos_)); -} - -int mg_mqtt_parse(const uint8_t *buf, size_t len, uint8_t version, - struct mg_mqtt_message *m) { - uint8_t lc = 0, *p, *end; - uint32_t n = 0, len_len = 0; - - memset(m, 0, sizeof(*m)); - m->dgram.buf = (char *) buf; - if (len < 2) return MQTT_INCOMPLETE; - m->cmd = (uint8_t) (buf[0] >> 4); - m->qos = (buf[0] >> 1) & 3; - - n = len_len = 0; - p = (uint8_t *) buf + 1; - while ((size_t) (p - buf) < len) { - lc = *((uint8_t *) p++); - n += (uint32_t) ((lc & 0x7f) << 7 * len_len); - len_len++; - if (!(lc & 0x80)) break; - if (len_len >= 4) return MQTT_MALFORMED; - } - end = p + n; - if ((lc & 0x80) || (end > buf + len)) return MQTT_INCOMPLETE; - m->dgram.len = (size_t) (end - buf); - - switch (m->cmd) { - case MQTT_CMD_CONNACK: - if (end - p < 2) return MQTT_MALFORMED; - m->ack = p[1]; - break; - case MQTT_CMD_PUBACK: - case MQTT_CMD_PUBREC: - case MQTT_CMD_PUBREL: - case MQTT_CMD_PUBCOMP: - case MQTT_CMD_SUBSCRIBE: - case MQTT_CMD_SUBACK: - case MQTT_CMD_UNSUBSCRIBE: - case MQTT_CMD_UNSUBACK: - if (p + 2 > end) return MQTT_MALFORMED; - m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); - p += 2; - break; - case MQTT_CMD_PUBLISH: { - if (p + 2 > end) return MQTT_MALFORMED; - m->topic.len = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); - m->topic.buf = (char *) p + 2; - p += 2 + m->topic.len; - if (p > end) return MQTT_MALFORMED; - if (m->qos > 0) { - if (p + 2 > end) return MQTT_MALFORMED; - m->id = (uint16_t) ((((uint16_t) p[0]) << 8) | p[1]); - p += 2; - } - if (p > end) return MQTT_MALFORMED; - if (version == 5 && p + 2 < end) { - len_len = - (uint32_t) decode_varint(p, (size_t) (end - p), &m->props_size); - if (!len_len) return MQTT_MALFORMED; - m->props_start = (size_t) (p + len_len - buf); - p += len_len + m->props_size; - } - if (p > end) return MQTT_MALFORMED; - m->data.buf = (char *) p; - m->data.len = (size_t) (end - p); - break; - } - default: - break; - } - return MQTT_OK; -} - -static void mqtt_cb(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { - for (;;) { - uint8_t version = c->is_mqtt5 ? 5 : 4; - struct mg_mqtt_message mm; - int rc = mg_mqtt_parse(c->recv.buf, c->recv.len, version, &mm); - if (rc == MQTT_MALFORMED) { - MG_ERROR(("%lu MQTT malformed message", c->id)); - c->is_closing = 1; - break; - } else if (rc == MQTT_OK) { - MG_VERBOSE(("%lu MQTT CMD %d len %d [%.*s]", c->id, mm.cmd, - (int) mm.dgram.len, (int) mm.data.len, mm.data.buf)); - switch (mm.cmd) { - case MQTT_CMD_CONNACK: - mg_call(c, MG_EV_MQTT_OPEN, &mm.ack); - if (mm.ack == 0) { - MG_DEBUG(("%lu Connected", c->id)); - } else { - MG_ERROR(("%lu MQTT auth failed, code %d", c->id, mm.ack)); - c->is_closing = 1; - } - break; - case MQTT_CMD_PUBLISH: { - MG_DEBUG(("%lu [%.*s] -> [%.*s%c", c->id, (int) mm.topic.len, - mm.topic.buf, - (int) (mm.data.len <= 10 ? mm.data.len : 10), mm.data.buf, - mm.data.len <= 10 ? ']' : ' ')); - if (mm.qos > 0) { - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); - if (c->is_mqtt5) remaining_len += 2; // 3.4.2 - - mg_mqtt_send_header( - c, - (uint8_t) (mm.qos == 2 ? MQTT_CMD_PUBREC : MQTT_CMD_PUBACK), - 0, remaining_len); - mg_send(c, &id, sizeof(id)); - - if (c->is_mqtt5) { - uint16_t zero = 0; - mg_send(c, &zero, sizeof(zero)); - } - } - mg_call(c, MG_EV_MQTT_MSG, &mm); // let the app handle qos stuff - break; - } - case MQTT_CMD_PUBREC: { // MQTT5: 3.5.2-1 TODO(): variable header rc - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); // MQTT5 3.6.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBREL, 2, remaining_len); - mg_send(c, &id, sizeof(id)); // MQTT5 3.6.1-1, flags = 2 - break; - } - case MQTT_CMD_PUBREL: { // MQTT5: 3.6.2-1 TODO(): variable header rc - uint16_t id = mg_ntohs(mm.id); - uint32_t remaining_len = sizeof(id); // MQTT5 3.7.2-1 - mg_mqtt_send_header(c, MQTT_CMD_PUBCOMP, 0, remaining_len); - mg_send(c, &id, sizeof(id)); - break; - } - } - mg_call(c, MG_EV_MQTT_CMD, &mm); - mg_iobuf_del(&c->recv, 0, mm.dgram.len); - } else { - break; - } - } - } - (void) ev_data; -} - -void mg_mqtt_ping(struct mg_connection *nc) { - mg_mqtt_send_header(nc, MQTT_CMD_PINGREQ, 0, 0); -} - -void mg_mqtt_pong(struct mg_connection *nc) { - mg_mqtt_send_header(nc, MQTT_CMD_PINGRESP, 0, 0); -} - -void mg_mqtt_disconnect(struct mg_connection *c, - const struct mg_mqtt_opts *opts) { - size_t len = 0; - if (c->is_mqtt5) len = 1 + get_props_size(opts->props, opts->num_props); - mg_mqtt_send_header(c, MQTT_CMD_DISCONNECT, 0, (uint32_t) len); - - if (c->is_mqtt5) { - uint8_t zero = 0; - mg_send(c, &zero, sizeof(zero)); // reason code - mg_send_mqtt_properties(c, opts->props, opts->num_props); - } -} - -struct mg_connection *mg_mqtt_connect(struct mg_mgr *mgr, const char *url, - const struct mg_mqtt_opts *opts, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_connect(mgr, url, fn, fn_data); - if (c != NULL) { - struct mg_mqtt_opts empty; - memset(&empty, 0, sizeof(empty)); - mg_mqtt_login(c, opts == NULL ? &empty : opts); - c->pfn = mqtt_cb; - } - return c; -} - -struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_listen(mgr, url, fn, fn_data); - if (c != NULL) c->pfn = mqtt_cb, c->pfn_data = mgr; - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/net.c" -#endif - - - - - - - - - -size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) { - size_t old = c->send.len; - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - return c->send.len - old; -} - -size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vprintf(c, fmt, &ap); - va_end(ap); - return len; -} - -static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { - uint32_t localhost = mg_htonl(0x7f000001); - if (mg_strcasecmp(str, mg_str("localhost")) != 0) return false; - memcpy(addr->ip, &localhost, sizeof(uint32_t)); - addr->is_ip6 = false; - return true; -} - -static bool mg_atone(struct mg_str str, struct mg_addr *addr) { - if (str.len > 0) return false; - memset(addr->ip, 0, sizeof(addr->ip)); - addr->is_ip6 = false; - return true; -} - -static bool mg_aton4(struct mg_str str, struct mg_addr *addr) { - uint8_t data[4] = {0, 0, 0, 0}; - size_t i, num_dots = 0; - for (i = 0; i < str.len; i++) { - if (str.buf[i] >= '0' && str.buf[i] <= '9') { - int octet = data[num_dots] * 10 + (str.buf[i] - '0'); - if (octet > 255) return false; - data[num_dots] = (uint8_t) octet; - } else if (str.buf[i] == '.') { - if (num_dots >= 3 || i == 0 || str.buf[i - 1] == '.') return false; - num_dots++; - } else { - return false; - } - } - if (num_dots != 3 || str.buf[i - 1] == '.') return false; - memcpy(&addr->ip, data, sizeof(data)); - addr->is_ip6 = false; - return true; -} - -static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) { - int i; - uint32_t ipv4; - if (str.len < 14) return false; - if (str.buf[0] != ':' || str.buf[1] != ':' || str.buf[6] != ':') return false; - for (i = 2; i < 6; i++) { - if (str.buf[i] != 'f' && str.buf[i] != 'F') return false; - } - // struct mg_str s = mg_str_n(&str.buf[7], str.len - 7); - if (!mg_aton4(mg_str_n(&str.buf[7], str.len - 7), addr)) return false; - memcpy(&ipv4, addr->ip, sizeof(ipv4)); - memset(addr->ip, 0, sizeof(addr->ip)); - addr->ip[10] = addr->ip[11] = 255; - memcpy(&addr->ip[12], &ipv4, 4); - addr->is_ip6 = true; - return true; -} - -static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { - size_t i, j = 0, n = 0, dc = 42; - addr->scope_id = 0; - if (str.len > 2 && str.buf[0] == '[') str.buf++, str.len -= 2; - if (mg_v4mapped(str, addr)) return true; - for (i = 0; i < str.len; i++) { - if ((str.buf[i] >= '0' && str.buf[i] <= '9') || - (str.buf[i] >= 'a' && str.buf[i] <= 'f') || - (str.buf[i] >= 'A' && str.buf[i] <= 'F')) { - unsigned long val = 0; // TODO(): This loops on chars, refactor - if (i > j + 3) return false; - // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.buf[j])); - mg_str_to_num(mg_str_n(&str.buf[j], i - j + 1), 16, &val, sizeof(val)); - addr->ip[n] = (uint8_t) ((val >> 8) & 255); - addr->ip[n + 1] = (uint8_t) (val & 255); - } else if (str.buf[i] == ':') { - j = i + 1; - if (i > 0 && str.buf[i - 1] == ':') { - dc = n; // Double colon - if (i > 1 && str.buf[i - 2] == ':') return false; - } else if (i > 0) { - n += 2; - } - if (n > 14) return false; - addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: - } else if (str.buf[i] == '%') { // Scope ID, last in string - return mg_str_to_num(mg_str_n(&str.buf[i + 1], str.len - i - 1), 10, - &addr->scope_id, sizeof(uint8_t)); - } else { - return false; - } - } - if (n < 14 && dc == 42) return false; - if (n < 14) { - memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); - memset(&addr->ip[dc], 0, 14 - n); - } - - addr->is_ip6 = true; - return true; -} - -bool mg_aton(struct mg_str str, struct mg_addr *addr) { - // MG_INFO(("[%.*s]", (int) str.len, str.buf)); - return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) || - mg_aton6(str, addr); -} - -struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) { - struct mg_connection *c = - (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize); - if (c != NULL) { - c->mgr = mgr; - c->send.align = c->recv.align = c->rtls.align = MG_IO_SIZE; - c->id = ++mgr->nextid; - MG_PROF_INIT(c); - } - return c; -} - -void mg_close_conn(struct mg_connection *c) { - mg_resolve_cancel(c); // Close any pending DNS query - LIST_DELETE(struct mg_connection, &c->mgr->conns, c); - if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL; - if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL; - // Order of operations is important. `MG_EV_CLOSE` event must be fired - // before we deallocate received data, see #1331 - mg_call(c, MG_EV_CLOSE, NULL); - MG_DEBUG(("%lu %ld closed", c->id, c->fd)); - MG_PROF_DUMP(c); - MG_PROF_FREE(c); - - mg_tls_free(c); - mg_iobuf_free(&c->recv); - mg_iobuf_free(&c->send); - mg_iobuf_free(&c->rtls); - mg_bzero((unsigned char *) c, sizeof(*c)); - free(c); -} - -struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = NULL; - if (url == NULL || url[0] == '\0') { - MG_ERROR(("null url")); - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM")); - } else { - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->is_udp = (strncmp(url, "udp:", 4) == 0); - c->fd = (void *) (size_t) MG_INVALID_SOCKET; - c->fn = fn; - c->is_client = true; - c->fn_data = fn_data; - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - mg_call(c, MG_EV_OPEN, (void *) url); - mg_resolve(c, url); - } - return c; -} - -struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = NULL; - if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM %s", url)); - } else if (!mg_open_listener(c, url)) { - MG_ERROR(("Failed: %s, errno %d", url, errno)); - MG_PROF_FREE(c); - free(c); - c = NULL; - } else { - c->is_listening = 1; - c->is_udp = strncmp(url, "udp:", 4) == 0; - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fn = fn; - c->fn_data = fn_data; - mg_call(c, MG_EV_OPEN, NULL); - if (mg_url_is_ssl(url)) c->is_tls = 1; // Accepted connection must - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - } - return c; -} - -struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_alloc_conn(mgr); - if (c != NULL) { - c->fd = (void *) (size_t) fd; - c->fn = fn; - c->fn_data = fn_data; - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_OPEN, NULL); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - } - return c; -} - -struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, - unsigned flags, void (*fn)(void *), void *arg) { - struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t)); - if (t != NULL) { - mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg); - t->id = mgr->timerid++; - } - return t; -} - -long mg_io_recv(struct mg_connection *c, void *buf, size_t len) { - if (c->rtls.len == 0) return MG_IO_WAIT; - if (len > c->rtls.len) len = c->rtls.len; - memcpy(buf, c->rtls.buf, len); - mg_iobuf_del(&c->rtls, 0, len); - return (long) len; -} - -void mg_mgr_free(struct mg_mgr *mgr) { - struct mg_connection *c; - struct mg_timer *tmp, *t = mgr->timers; - while (t != NULL) tmp = t->next, free(t), t = tmp; - mgr->timers = NULL; // Important. Next call to poll won't touch timers - for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1; - mg_mgr_poll(mgr, 0); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_DeleteSocketSet(mgr->ss); -#endif - MG_DEBUG(("All connections closed")); -#if MG_ENABLE_EPOLL - if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1; -#endif - mg_tls_ctx_free(mgr); -} - -void mg_mgr_init(struct mg_mgr *mgr) { - memset(mgr, 0, sizeof(*mgr)); -#if MG_ENABLE_EPOLL - if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) - MG_ERROR(("epoll_create1 errno %d", errno)); -#else - mgr->epoll_fd = -1; -#endif -#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - // clang-format off - { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } - // clang-format on -#elif MG_ENABLE_FREERTOS_TCP - mgr->ss = FreeRTOS_CreateSocketSet(); -#elif defined(__unix) || defined(__unix__) || defined(__APPLE__) - // Ignore SIGPIPE signal, so if client cancels the request, it - // won't kill the whole process. - signal(SIGPIPE, SIG_IGN); -#elif MG_ENABLE_TCPIP_DRIVER_INIT && defined(MG_TCPIP_DRIVER_INIT) - MG_TCPIP_DRIVER_INIT(mgr); -#endif - mgr->pipe = MG_INVALID_SOCKET; - mgr->dnstimeout = 3000; - mgr->dns4.url = "udp://8.8.8.8:53"; - mgr->dns6.url = "udp://[2001:4860:4860::8888]:53"; - mg_tls_ctx_init(mgr); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/net_builtin.c" -#endif - - -#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP -#define MG_EPHEMERAL_PORT_BASE 32768 -#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a)))) - -#ifndef MIP_TCP_KEEPALIVE_MS -#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms -#endif - -#define MIP_TCP_ACK_MS 150 // Timeout for ACKing -#define MIP_ARP_RESP_MS 100 // Timeout for ARP response -#define MIP_TCP_SYN_MS 15000 // Timeout for connection establishment -#define MIP_TCP_FIN_MS 1000 // Timeout for closing connection -#define MIP_TCP_WIN 6000 // TCP window size - -struct connstate { - uint32_t seq, ack; // TCP seq/ack counters - uint64_t timer; // TCP keep-alive / ACK timer - uint32_t acked; // Last ACK-ed number - size_t unacked; // Not acked bytes - uint8_t mac[6]; // Peer MAC address - uint8_t ttype; // Timer type. 0: ack, 1: keep-alive -#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive -#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon -#define MIP_TTYPE_ARP 2 // ARP resolve sent, waiting for response -#define MIP_TTYPE_SYN 3 // SYN sent, waiting for response -#define MIP_TTYPE_FIN 4 // FIN sent, waiting until terminating the connection - uint8_t tmiss; // Number of keep-alive misses - struct mg_iobuf raw; // For TLS only. Incoming raw data -}; - -#pragma pack(push, 1) - -struct lcp { - uint8_t addr, ctrl, proto[2], code, id, len[2]; -}; - -struct eth { - uint8_t dst[6]; // Destination MAC address - uint8_t src[6]; // Source MAC address - uint16_t type; // Ethernet type -}; - -struct ip { - uint8_t ver; // Version - uint8_t tos; // Unused - uint16_t len; // Length - uint16_t id; // Unused - uint16_t frag; // Fragmentation -#define IP_FRAG_OFFSET_MSK 0x1fff -#define IP_MORE_FRAGS_MSK 0x2000 - uint8_t ttl; // Time to live - uint8_t proto; // Upper level protocol - uint16_t csum; // Checksum - uint32_t src; // Source IP - uint32_t dst; // Destination IP -}; - -struct ip6 { - uint8_t ver; // Version - uint8_t opts[3]; // Options - uint16_t len; // Length - uint8_t proto; // Upper level protocol - uint8_t ttl; // Time to live - uint8_t src[16]; // Source IP - uint8_t dst[16]; // Destination IP -}; - -struct icmp { - uint8_t type; - uint8_t code; - uint16_t csum; -}; - -struct arp { - uint16_t fmt; // Format of hardware address - uint16_t pro; // Format of protocol address - uint8_t hlen; // Length of hardware address - uint8_t plen; // Length of protocol address - uint16_t op; // Operation - uint8_t sha[6]; // Sender hardware address - uint32_t spa; // Sender protocol address - uint8_t tha[6]; // Target hardware address - uint32_t tpa; // Target protocol address -}; - -struct tcp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint32_t seq; // Sequence number - uint32_t ack; // Acknowledgement number - uint8_t off; // Data offset - uint8_t flags; // TCP flags -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 -#define TH_ECE 0x40 -#define TH_CWR 0x80 - uint16_t win; // Window - uint16_t csum; // Checksum - uint16_t urp; // Urgent pointer -}; - -struct udp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint16_t len; // UDP length - uint16_t csum; // UDP checksum -}; - -struct dhcp { - uint8_t op, htype, hlen, hops; - uint32_t xid; - uint16_t secs, flags; - uint32_t ciaddr, yiaddr, siaddr, giaddr; - uint8_t hwaddr[208]; - uint32_t magic; - uint8_t options[32]; -}; - -#pragma pack(pop) - -struct pkt { - struct mg_str raw; // Raw packet data - struct mg_str pay; // Payload data - struct eth *eth; - struct llc *llc; - struct arp *arp; - struct ip *ip; - struct ip6 *ip6; - struct icmp *icmp; - struct tcp *tcp; - struct udp *udp; - struct dhcp *dhcp; -}; - -static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) { - if (ifp->fn != NULL) ifp->fn(ifp, ev, ev_data); -} - -static void send_syn(struct mg_connection *c); - -static void mkpay(struct pkt *pkt, void *p) { - pkt->pay = - mg_str_n((char *) p, (size_t) (&pkt->raw.buf[pkt->raw.len] - (char *) p)); -} - -static uint32_t csumup(uint32_t sum, const void *buf, size_t len) { - size_t i; - const uint8_t *p = (const uint8_t *) buf; - for (i = 0; i < len; i++) sum += i & 1 ? p[i] : ((uint32_t) p[i]) << 8; - return sum; -} - -static uint16_t csumfin(uint32_t sum) { - while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); - return mg_htons(~sum & 0xffff); -} - -static uint16_t ipcsum(const void *buf, size_t len) { - uint32_t sum = csumup(0, buf, len); - return csumfin(sum); -} - -static void settmout(struct mg_connection *c, uint8_t type) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - struct connstate *s = (struct connstate *) (c + 1); - unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS - : type == MIP_TTYPE_ARP ? MIP_ARP_RESP_MS - : type == MIP_TTYPE_SYN ? MIP_TCP_SYN_MS - : type == MIP_TTYPE_FIN ? MIP_TCP_FIN_MS - : MIP_TCP_KEEPALIVE_MS; - s->timer = ifp->now + n; - s->ttype = type; - MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer)); -} - -static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) { - size_t n = ifp->driver->tx(ifp->tx.buf, len, ifp); - if (n == len) ifp->nsent++; - return n; -} - -void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac) { - struct eth *eth = (struct eth *) ifp->tx.buf; - struct arp *arp = (struct arp *) (eth + 1); - memset(eth->dst, 255, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = mg_htons(0x806); - memset(arp, 0, sizeof(*arp)); - arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6, - arp->plen = 4; - arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip; - memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); - if (mac != NULL) memcpy(arp->tha, mac, sizeof(arp->tha)); - ether_output(ifp, PDIFF(eth, arp + 1)); -} - -static void onstatechange(struct mg_tcpip_if *ifp) { - if (ifp->state == MG_TCPIP_STATE_READY) { - MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip)); - MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw)); - MG_INFO((" MAC: %M", mg_print_mac, &ifp->mac)); - } else if (ifp->state == MG_TCPIP_STATE_IP) { - MG_ERROR(("Got IP")); - mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request - } else if (ifp->state == MG_TCPIP_STATE_UP) { - MG_ERROR(("Link up")); - srand((unsigned int) mg_millis()); - } else if (ifp->state == MG_TCPIP_STATE_DOWN) { - MG_ERROR(("Link down")); - } - mg_tcpip_call(ifp, MG_TCPIP_EV_STATE_CHANGE, &ifp->state); -} - -static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst, - uint8_t proto, uint32_t ip_src, uint32_t ip_dst, - size_t plen) { - struct eth *eth = (struct eth *) ifp->tx.buf; - struct ip *ip = (struct ip *) (eth + 1); - memcpy(eth->dst, mac_dst, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC - eth->type = mg_htons(0x800); - memset(ip, 0, sizeof(*ip)); - ip->ver = 0x45; // Version 4, header length 5 words - ip->frag = mg_htons(0x4000); // Don't fragment - ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen)); - ip->ttl = 64; - ip->proto = proto; - ip->src = ip_src; - ip->dst = ip_dst; - ip->csum = ipcsum(ip, sizeof(*ip)); - return ip; -} - -static void tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, - uint16_t sport, uint32_t ip_dst, uint16_t dport, - const void *buf, size_t len) { - struct ip *ip = - tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp)); - struct udp *udp = (struct udp *) (ip + 1); - // MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len)); - udp->sport = sport; - udp->dport = dport; - udp->len = mg_htons((uint16_t) (sizeof(*udp) + len)); - udp->csum = 0; - uint32_t cs = csumup(0, udp, sizeof(*udp)); - cs = csumup(cs, buf, len); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs += (uint32_t) (ip->proto + sizeof(*udp) + len); - udp->csum = csumfin(cs); - memmove(udp + 1, buf, len); - // MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len)); - ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len); -} - -static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src, - uint32_t ip_dst, uint8_t *opts, size_t optslen, - bool ciaddr) { - // https://datatracker.ietf.org/doc/html/rfc2132#section-9.6 - struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; - dhcp.magic = mg_htonl(0x63825363); - memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac)); - memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid)); - memcpy(&dhcp.options, opts, optslen); - if (ciaddr) dhcp.ciaddr = ip_src; - tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp, - sizeof(dhcp)); -} - -static const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; - -// RFC-2131 #4.3.6, #4.4.1; RFC-2132 #9.8 -static void tx_dhcp_request_sel(struct mg_tcpip_if *ifp, uint32_t ip_req, - uint32_t ip_srv) { - uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 54, 4, 0, 0, 0, 0, // DHCP server ID - 50, 4, 0, 0, 0, 0, // Requested IP - 55, 2, 1, 3, 255, 255, // GW, mask [DNS] [SNTP] - 255 // End of options - }; - uint8_t addopts = 0; - memcpy(opts + 10, &ip_srv, sizeof(ip_srv)); - memcpy(opts + 16, &ip_req, sizeof(ip_req)); - if (ifp->enable_req_dns) opts[24 + addopts++] = 6; // DNS - if (ifp->enable_req_sntp) opts[24 + addopts++] = 42; // SNTP - opts[21] += addopts; - tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, - sizeof(opts) + addopts - 2, false); - MG_DEBUG(("DHCP req sent")); -} - -// RFC-2131 #4.3.6, #4.4.5 (renewing: unicast, rebinding: bcast) -static void tx_dhcp_request_re(struct mg_tcpip_if *ifp, uint8_t *mac_dst, - uint32_t ip_src, uint32_t ip_dst) { - uint8_t opts[] = { - 53, 1, 3, // Type: DHCP request - 255 // End of options - }; - tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts), true); - MG_DEBUG(("DHCP req sent")); -} - -static void tx_dhcp_discover(struct mg_tcpip_if *ifp) { - uint8_t opts[] = { - 53, 1, 1, // Type: DHCP discover - 55, 2, 1, 3, // Parameters: ip, mask - 255 // End of options - }; - tx_dhcp(ifp, (uint8_t *) broadcast, 0, 0xffffffff, opts, sizeof(opts), false); - MG_DEBUG(("DHCP discover sent. Our MAC: %M", mg_print_mac, ifp->mac)); -} - -static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, - bool lsn) { - struct mg_connection *c = NULL; - for (c = mgr->conns; c != NULL; c = c->next) { - if (c->is_arplooking && pkt->arp && - memcmp(&pkt->arp->spa, c->rem.ip, sizeof(pkt->arp->spa)) == 0) - break; - if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break; - if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport && - lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport)) - break; - } - return c; -} - -static void mac_resolved(struct mg_connection *c); - -static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) { - // ARP request. Make a response, then send - // MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4, - // &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa)); - struct eth *eth = (struct eth *) ifp->tx.buf; - struct arp *arp = (struct arp *) (eth + 1); - memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = mg_htons(0x806); - *arp = *pkt->arp; - arp->op = mg_htons(2); - memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha)); - memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha)); - arp->tpa = pkt->arp->spa; - arp->spa = ifp->ip; - MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_mac, - &ifp->mac)); - ether_output(ifp, PDIFF(eth, arp + 1)); - } else if (pkt->arp->op == mg_htons(2)) { - if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; - if (pkt->arp->spa == ifp->gw) { - // Got response for the GW ARP request. Set ifp->gwmac and IP -> READY - memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac)); - if (ifp->state == MG_TCPIP_STATE_IP) { - ifp->state = MG_TCPIP_STATE_READY; - onstatechange(ifp); - } - } else { - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); - if (c != NULL && c->is_arplooking) { - struct connstate *s = (struct connstate *) (c + 1); - memcpy(s->mac, pkt->arp->sha, sizeof(s->mac)); - MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, c->rem.ip, - mg_print_mac, s->mac)); - c->is_arplooking = 0; - mac_resolved(c); - } - } - } -} - -static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("ICMP %d", (int) len)); - if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) { - size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp); - size_t space = ifp->tx.len - hlen, plen = pkt->pay.len; - if (plen > space) plen = space; - struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src, - sizeof(struct icmp) + plen); - struct icmp *icmp = (struct icmp *) (ip + 1); - memset(icmp, 0, sizeof(*icmp)); // Set csum to 0 - memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX - icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen); - ether_output(ifp, hlen + plen); - } -} - -static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint32_t ip = 0, gw = 0, mask = 0, lease = 0, dns = 0, sntp = 0; - uint8_t msgtype = 0, state = ifp->state; - // perform size check first, then access fields - uint8_t *p = pkt->dhcp->options, - *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len]; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; - if (memcmp(&pkt->dhcp->xid, ifp->mac + 2, sizeof(pkt->dhcp->xid))) return; - while (p + 1 < end && p[0] != 255) { // Parse options RFC-1533 #9 - if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask - memcpy(&mask, p + 2, sizeof(mask)); - } else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW - memcpy(&gw, p + 2, sizeof(gw)); - ip = pkt->dhcp->yiaddr; - } else if (ifp->enable_req_dns && p[0] == 6 && p[1] == sizeof(dns) && - p + 6 < end) { // DNS - memcpy(&dns, p + 2, sizeof(dns)); - } else if (ifp->enable_req_sntp && p[0] == 42 && p[1] == sizeof(sntp) && - p + 6 < end) { // SNTP - memcpy(&sntp, p + 2, sizeof(sntp)); - } else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease - memcpy(&lease, p + 2, sizeof(lease)); - lease = mg_ntohl(lease); - } else if (p[0] == 53 && p[1] == 1 && p + 6 < end) { // Msg Type - msgtype = p[2]; - } - p += p[1] + 2; - } - // Process message type, RFC-1533 (9.4); RFC-2131 (3.1, 4) - if (msgtype == 6 && ifp->ip == ip) { // DHCPNACK, release IP - ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; - } else if (msgtype == 2 && ifp->state == MG_TCPIP_STATE_UP && ip && gw && - lease) { // DHCPOFFER - // select IP, (4.4.1) (fallback to IP source addr on foul play) - tx_dhcp_request_sel(ifp, ip, - pkt->dhcp->siaddr ? pkt->dhcp->siaddr : pkt->ip->src); - ifp->state = MG_TCPIP_STATE_REQ; // REQUESTING state - } else if (msgtype == 5) { // DHCPACK - if (ifp->state == MG_TCPIP_STATE_REQ && ip && gw && lease) { // got an IP - ifp->lease_expire = ifp->now + lease * 1000; - MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - // assume DHCP server = router until ARP resolves - memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); - ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; - ifp->state = MG_TCPIP_STATE_IP; // BOUND state - uint64_t rand; - mg_random(&rand, sizeof(rand)); - srand((unsigned int) (rand + mg_millis())); - if (ifp->enable_req_dns && dns != 0) - mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_DNS, &dns); - if (ifp->enable_req_sntp && sntp != 0) - mg_tcpip_call(ifp, MG_TCPIP_EV_DHCP_SNTP, &sntp); - } else if (ifp->state == MG_TCPIP_STATE_READY && ifp->ip == ip) { // renew - ifp->lease_expire = ifp->now + lease * 1000; - MG_INFO(("Lease: %u sec (%lld)", lease, ifp->lease_expire / 1000)); - } // TODO(): accept provided T1/T2 and store server IP for renewal (4.4) - } - if (ifp->state != state) onstatechange(ifp); -} - -// Simple DHCP server that assigns a next IP address: ifp->ip + 1 -static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint8_t op = 0, *p = pkt->dhcp->options, - *end = (uint8_t *) &pkt->raw.buf[pkt->raw.len]; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; - // struct dhcp *req = pkt->dhcp; - struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}}; - res.yiaddr = ifp->ip; - ((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1 - while (p + 1 < end && p[0] != 255) { // Parse options - if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type - op = p[2]; - } - p += p[1] + 2; - } - if (op == 1 || op == 3) { // DHCP Discover or DHCP Request - uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK - uint8_t opts[] = { - 53, 1, msg, // Message type - 1, 4, 0, 0, 0, 0, // Subnet mask - 54, 4, 0, 0, 0, 0, // Server ID - 12, 3, 'm', 'i', 'p', // Host name: "mip" - 51, 4, 255, 255, 255, 255, // Lease time - 255 // End of options - }; - memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6); - memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask)); - memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip)); - memcpy(&res.options, opts, sizeof(opts)); - res.magic = pkt->dhcp->magic; - res.xid = pkt->dhcp->xid; - if (ifp->enable_get_gateway) { - ifp->gw = res.yiaddr; // set gw IP, best-effort gwmac as DHCP server's - memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac)); - } - tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67), - op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res)); - } -} - -static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct mg_connection *c = getpeer(ifp->mgr, pkt, true); - if (c == NULL) { - // No UDP listener on this port. Should send ICMP, but keep silent. - } else { - c->rem.port = pkt->udp->sport; - memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - struct connstate *s = (struct connstate *) (c + 1); - memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); - if (c->recv.len >= MG_MAX_RECV_SIZE) { - mg_error(c, "max_recv_buf_size reached"); - } else if (c->recv.size - c->recv.len < pkt->pay.len && - !mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) { - mg_error(c, "oom"); - } else { - memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); - c->recv.len += pkt->pay.len; - mg_call(c, MG_EV_READ, &pkt->pay.len); - } - } -} - -static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip, - uint8_t flags, uint16_t sport, uint16_t dport, - uint32_t seq, uint32_t ack, const void *buf, size_t len) { -#if 0 - uint8_t opts[] = {2, 4, 5, 0xb4, 4, 2, 0, 0}; // MSS = 1460, SACK permitted - if (flags & TH_SYN) { - // Handshake? Set MSS - buf = opts; - len = sizeof(opts); - } -#endif - struct ip *ip = - tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len); - struct tcp *tcp = (struct tcp *) (ip + 1); - memset(tcp, 0, sizeof(*tcp)); - if (buf != NULL && len) memmove(tcp + 1, buf, len); - tcp->sport = sport; - tcp->dport = dport; - tcp->seq = seq; - tcp->ack = ack; - tcp->flags = flags; - tcp->win = mg_htons(MIP_TCP_WIN); - tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4); - // if (flags & TH_SYN) tcp->off = 0x70; // Handshake? header size 28 bytes - - uint32_t cs = 0; - uint16_t n = (uint16_t) (sizeof(*tcp) + len); - uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)}; - cs = csumup(cs, tcp, n); - cs = csumup(cs, &ip->src, sizeof(ip->src)); - cs = csumup(cs, &ip->dst, sizeof(ip->dst)); - cs = csumup(cs, pseudo, sizeof(pseudo)); - tcp->csum = csumfin(cs); - MG_VERBOSE(("TCP %M:%hu -> %M:%hu fl %x len %u", mg_print_ip4, &ip->src, - mg_ntohs(tcp->sport), mg_print_ip4, &ip->dst, - mg_ntohs(tcp->dport), tcp->flags, len)); - // mg_hexdump(ifp->tx.buf, PDIFF(ifp->tx.buf, tcp + 1) + len); - return ether_output(ifp, PDIFF(ifp->tx.buf, tcp + 1) + len); -} - -static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt, - uint8_t flags, uint32_t seq, const void *buf, - size_t len) { - uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0; - return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport, - pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta), - buf, len); -} - -static struct mg_connection *accept_conn(struct mg_connection *lsn, - struct pkt *pkt) { - struct mg_connection *c = mg_alloc_conn(lsn->mgr); - if (c == NULL) { - MG_ERROR(("OOM")); - return NULL; - } - struct connstate *s = (struct connstate *) (c + 1); - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); - memcpy(s->mac, pkt->eth->src, sizeof(s->mac)); - settmout(c, MIP_TTYPE_KEEPALIVE); - memcpy(c->rem.ip, &pkt->ip->src, sizeof(uint32_t)); - c->rem.port = pkt->tcp->sport; - MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem)); - LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c); - c->is_accepted = 1; - c->is_hexdumping = lsn->is_hexdumping; - c->pfn = lsn->pfn; - c->loc = lsn->loc; - c->pfn_data = lsn->pfn_data; - c->fn = lsn->fn; - c->fn_data = lsn->fn_data; - mg_call(c, MG_EV_OPEN, NULL); - mg_call(c, MG_EV_ACCEPT, NULL); - return c; -} - -static size_t trim_len(struct mg_connection *c, size_t len) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - size_t eth_h_len = 14, ip_max_h_len = 24, tcp_max_h_len = 60, udp_h_len = 8; - size_t max_headers_len = - eth_h_len + ip_max_h_len + (c->is_udp ? udp_h_len : tcp_max_h_len); - size_t min_mtu = c->is_udp ? 68 /* RFC-791 */ : max_headers_len - eth_h_len; - - // If the frame exceeds the available buffer, trim the length - if (len + max_headers_len > ifp->tx.len) { - len = ifp->tx.len - max_headers_len; - } - // Ensure the MTU isn't lower than the minimum allowed value - if (ifp->mtu < min_mtu) { - MG_ERROR(("MTU is lower than minimum, capping to %lu", min_mtu)); - ifp->mtu = (uint16_t) min_mtu; - } - // If the total packet size exceeds the MTU, trim the length - if (len + max_headers_len - eth_h_len > ifp->mtu) { - len = ifp->mtu - max_headers_len + eth_h_len; - if (c->is_udp) { - MG_ERROR(("UDP datagram exceeds MTU. Truncating it.")); - } - } - - return len; -} - -long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - struct connstate *s = (struct connstate *) (c + 1); - uint32_t dst_ip = *(uint32_t *) c->rem.ip; - len = trim_len(c, len); - if (c->is_udp) { - tx_udp(ifp, s->mac, ifp->ip, c->loc.port, dst_ip, c->rem.port, buf, len); - } else { - size_t sent = - tx_tcp(ifp, s->mac, dst_ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), buf, len); - if (sent == 0) { - return MG_IO_WAIT; - } else if (sent == (size_t) -1) { - return MG_IO_ERR; - } else { - s->seq += (uint32_t) len; - if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE); - } - } - return (long) len; -} - -static void handle_tls_recv(struct mg_connection *c) { - size_t avail = mg_tls_pending(c); - size_t min = avail > MG_MAX_RECV_SIZE ? MG_MAX_RECV_SIZE : avail; - struct mg_iobuf *io = &c->recv; - if (io->size - io->len < min && !mg_iobuf_resize(io, io->len + min)) { - mg_error(c, "oom"); - } else { - // Decrypt data directly into c->recv - long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len); - if (n == MG_IO_ERR) { - mg_error(c, "TLS recv error"); - } else if (n > 0) { - // Decrypted successfully - trigger MG_EV_READ - io->len += (size_t) n; - mg_call(c, MG_EV_READ, &n); - } // else n < 0: outstanding data to be moved to c->recv - } -} - -static void read_conn(struct mg_connection *c, struct pkt *pkt) { - struct connstate *s = (struct connstate *) (c + 1); - struct mg_iobuf *io = c->is_tls ? &c->rtls : &c->recv; - uint32_t seq = mg_ntohl(pkt->tcp->seq); - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (pkt->tcp->flags & TH_FIN) { - // If we initiated the closure, we reply with ACK upon receiving FIN - // If we didn't initiate it, we reply with FIN as part of the normal TCP - // closure process - uint8_t flags = TH_ACK; - s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len + 1); - if (c->is_draining && s->ttype == MIP_TTYPE_FIN) { - if (s->seq == mg_htonl(pkt->tcp->ack)) { // Simultaneous closure ? - s->seq++; // Yes. Increment our SEQ - } else { // Otherwise, - s->seq = mg_htonl(pkt->tcp->ack); // Set to peer's ACK - } - } else { - flags |= TH_FIN; - c->is_draining = 1; - settmout(c, MIP_TTYPE_FIN); - } - tx_tcp(c->mgr->ifp, s->mac, rem_ip, flags, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } else if (pkt->pay.len == 0) { - // TODO(cpq): handle this peer's ACK - } else if (seq != s->ack) { - uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); - if (s->ack == ack) { - MG_VERBOSE(("ignoring duplicate pkt")); - } else { - MG_VERBOSE(("SEQ != ACK: %x %x %x", seq, s->ack, ack)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), "", 0); - } - } else if (io->size - io->len < pkt->pay.len && - !mg_iobuf_resize(io, io->len + pkt->pay.len)) { - mg_error(c, "oom"); - } else { - // Copy TCP payload into the IO buffer. If the connection is plain text, - // we copy to c->recv. If the connection is TLS, this data is encrypted, - // therefore we copy that encrypted data to the c->rtls iobuffer instead, - // and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will - // call back mg_io_recv() which grabs raw data from c->rtls - memcpy(&io->buf[io->len], pkt->pay.buf, pkt->pay.len); - io->len += pkt->pay.len; - - MG_VERBOSE(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack)); - // Advance ACK counter - s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len); - s->unacked += pkt->pay.len; - // size_t diff = s->acked <= s->ack ? s->ack - s->acked : s->ack; - if (s->unacked > MIP_TCP_WIN / 2 && s->acked != s->ack) { - // Send ACK immediately - MG_VERBOSE(("%lu imm ACK %lu", c->id, s->acked)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - s->unacked = 0; - s->acked = s->ack; - if (s->ttype != MIP_TTYPE_KEEPALIVE) settmout(c, MIP_TTYPE_KEEPALIVE); - } else { - // if not already running, setup a timer to send an ACK later - if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK); - } - - if (c->is_tls && c->is_tls_hs) { - mg_tls_handshake(c); - } else if (c->is_tls) { - handle_tls_recv(c); - } else { - // Plain text connection, data is already in c->recv, trigger MG_EV_READ - mg_call(c, MG_EV_READ, &pkt->pay.len); - } - } -} - -static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) { - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); - struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1); -#if 0 - MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); -#endif - if (c != NULL && c->is_connecting && pkt->tcp->flags == (TH_SYN | TH_ACK)) { - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1; - tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0); - c->is_connecting = 0; // Client connected - settmout(c, MIP_TTYPE_KEEPALIVE); - mg_call(c, MG_EV_CONNECT, NULL); // Let user know - if (c->is_tls_hs) mg_tls_handshake(c); - } else if (c != NULL && c->is_connecting && pkt->tcp->flags != TH_ACK) { - // mg_hexdump(pkt->raw.buf, pkt->raw.len); - tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); - } else if (c != NULL && pkt->tcp->flags & TH_RST) { - mg_error(c, "peer RST"); // RFC-1122 4.2.2.13 - } else if (c != NULL) { -#if 0 - MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len, - mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport), - mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport))); - mg_hexdump(pkt->pay.buf, pkt->pay.len); -#endif - s->tmiss = 0; // Reset missed keep-alive counter - if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer - settmout(c, - MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending - read_conn(c, pkt); // Override timer with ACK timeout if needed - } else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) { - tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); - } else if (pkt->tcp->flags & TH_RST) { - if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13 - // ignore RST if not connected - } else if (pkt->tcp->flags & TH_SYN) { - // Use peer's source port as ISN, in order to recognise the handshake - uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport)); - tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0); - } else if (pkt->tcp->flags & TH_FIN) { - tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0); - } else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) { - accept_conn(c, pkt); - } else if (!c->is_accepted) { // no peer - tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0); - } else { - // MG_VERBOSE(("dropped silently..")); - } -} - -static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) { - uint16_t frag = mg_ntohs(pkt->ip->frag); - if (frag & IP_MORE_FRAGS_MSK || frag & IP_FRAG_OFFSET_MSK) { - if (pkt->ip->proto == 17) pkt->udp = (struct udp *) (pkt->ip + 1); - if (pkt->ip->proto == 6) pkt->tcp = (struct tcp *) (pkt->ip + 1); - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); - if (c) mg_error(c, "Received fragmented packet"); - } else if (pkt->ip->proto == 1) { - pkt->icmp = (struct icmp *) (pkt->ip + 1); - if (pkt->pay.len < sizeof(*pkt->icmp)) return; - mkpay(pkt, pkt->icmp + 1); - rx_icmp(ifp, pkt); - } else if (pkt->ip->proto == 17) { - pkt->udp = (struct udp *) (pkt->ip + 1); - if (pkt->pay.len < sizeof(*pkt->udp)) return; - mkpay(pkt, pkt->udp + 1); - MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, - mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst, - mg_ntohs(pkt->udp->dport), (int) pkt->pay.len)); - if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68)) { - pkt->dhcp = (struct dhcp *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp_client(ifp, pkt); - } else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) { - pkt->dhcp = (struct dhcp *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp_server(ifp, pkt); - } else { - rx_udp(ifp, pkt); - } - } else if (pkt->ip->proto == 6) { - pkt->tcp = (struct tcp *) (pkt->ip + 1); - if (pkt->pay.len < sizeof(*pkt->tcp)) return; - mkpay(pkt, pkt->tcp + 1); - uint16_t iplen = mg_ntohs(pkt->ip->len); - uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U)); - if (iplen >= off) pkt->pay.len = (size_t) (iplen - off); - MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src, - mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst, - mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len)); - rx_tcp(ifp, pkt); - } -} - -static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("IP %d", (int) len)); - if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) { - pkt->icmp = (struct icmp *) (pkt->ip6 + 1); - if (pkt->pay.len < sizeof(*pkt->icmp)) return; - mkpay(pkt, pkt->icmp + 1); - rx_icmp(ifp, pkt); - } else if (pkt->ip6->proto == 17) { - pkt->udp = (struct udp *) (pkt->ip6 + 1); - if (pkt->pay.len < sizeof(*pkt->udp)) return; - // MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport), - // mg_htons(udp->dport))); - mkpay(pkt, pkt->udp + 1); - } -} - -static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) { - struct pkt pkt; - memset(&pkt, 0, sizeof(pkt)); - pkt.raw.buf = (char *) buf; - pkt.raw.len = len; - pkt.eth = (struct eth *) buf; - // mg_hexdump(buf, len > 16 ? 16: len); - if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt? - if (ifp->enable_mac_check && - memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 && - memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) - return; - if (ifp->enable_crc32_check && len > 4) { - len -= 4; // TODO(scaprile): check on bigendian - uint32_t crc = mg_crc32(0, (const char *) buf, len); - if (memcmp((void *) ((size_t) buf + len), &crc, sizeof(crc))) return; - } - if (pkt.eth->type == mg_htons(0x806)) { - pkt.arp = (struct arp *) (pkt.eth + 1); - if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated - mg_tcpip_call(ifp, MG_TCPIP_EV_ARP, &pkt.raw); - rx_arp(ifp, &pkt); - } else if (pkt.eth->type == mg_htons(0x86dd)) { - pkt.ip6 = (struct ip6 *) (pkt.eth + 1); - if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated - if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP - mkpay(&pkt, pkt.ip6 + 1); - rx_ip6(ifp, &pkt); - } else if (pkt.eth->type == mg_htons(0x800)) { - pkt.ip = (struct ip *) (pkt.eth + 1); - if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated - // Truncate frame to what IP header tells us - if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) { - pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth); - } - if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated - if ((pkt.ip->ver >> 4) != 4) return; // Not IP - mkpay(&pkt, pkt.ip + 1); - rx_ip(ifp, &pkt); - } else { - MG_DEBUG(("Unknown eth type %x", mg_htons(pkt.eth->type))); - if (mg_log_level >= MG_LL_VERBOSE) mg_hexdump(buf, len >= 32 ? 32 : len); - } -} - -static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) { - struct mg_connection *c; - bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, now); - ifp->now = now; - - if (expired_1000ms) { -#if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS - const char *names[] = {"down", "up", "req", "ip", "ready"}; - MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", - names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, - ifp->ndrop, ifp->nerr)); -#endif - } - // Handle gw ARP request timeout, order is important - if (expired_1000ms && ifp->state == MG_TCPIP_STATE_IP) { - ifp->state = MG_TCPIP_STATE_READY; // keep best-effort MAC - onstatechange(ifp); - } - // Handle physical interface up/down status - if (expired_1000ms && ifp->driver->up) { - bool up = ifp->driver->up(ifp); - bool current = ifp->state != MG_TCPIP_STATE_DOWN; - if (!up && ifp->enable_dhcp_client) ifp->ip = 0; - if (up != current) { // link state has changed - ifp->state = up == false ? MG_TCPIP_STATE_DOWN - : ifp->enable_dhcp_client || ifp->ip == 0 - ? MG_TCPIP_STATE_UP - : MG_TCPIP_STATE_IP; - onstatechange(ifp); - } else if (!ifp->enable_dhcp_client && ifp->state == MG_TCPIP_STATE_UP && - ifp->ip) { - ifp->state = MG_TCPIP_STATE_IP; // ifp->fn has set an IP - onstatechange(ifp); - } - if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down")); - mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL); - } - if (ifp->state == MG_TCPIP_STATE_DOWN) return; - - // DHCP RFC-2131 (4.4) - if (ifp->enable_dhcp_client && expired_1000ms) { - if (ifp->state == MG_TCPIP_STATE_UP) { - tx_dhcp_discover(ifp); // INIT (4.4.1) - } else if (ifp->state == MG_TCPIP_STATE_READY && - ifp->lease_expire > 0) { // BOUND / RENEWING / REBINDING - if (ifp->now >= ifp->lease_expire) { - ifp->state = MG_TCPIP_STATE_UP, ifp->ip = 0; // expired, release IP - onstatechange(ifp); - } else if (ifp->now + 30UL * 60UL * 1000UL > ifp->lease_expire && - ((ifp->now / 1000) % 60) == 0) { - // hack: 30 min before deadline, try to rebind (4.3.6) every min - tx_dhcp_request_re(ifp, (uint8_t *) broadcast, ifp->ip, 0xffffffff); - } // TODO(): Handle T1 (RENEWING) and T2 (REBINDING) (4.4.5) - } - } - - // Read data from the network - if (ifp->driver->rx != NULL) { // Polling driver. We must call it - size_t len = - ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp); - if (len > 0) { - ifp->nrecv++; - mg_tcpip_rx(ifp, ifp->recv_queue.buf, len); - } - } else { // Interrupt-based driver. Fills recv queue itself - char *buf; - size_t len = mg_queue_next(&ifp->recv_queue, &buf); - if (len > 0) { - mg_tcpip_rx(ifp, buf, len); - mg_queue_del(&ifp->recv_queue, len); - } - } - - // Process timeouts - for (c = ifp->mgr->conns; c != NULL; c = c->next) { - if ((c->is_udp && !c->is_arplooking) || c->is_listening || c->is_resolving) - continue; - struct connstate *s = (struct connstate *) (c + 1); - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (now > s->timer) { - if (s->ttype == MIP_TTYPE_ARP) { - mg_error(c, "ARP timeout"); - } else if (c->is_udp) { - continue; - } else if (s->ttype == MIP_TTYPE_ACK && s->acked != s->ack) { - MG_VERBOSE(("%lu ack %x %x", c->id, s->seq, s->ack)); - tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - s->acked = s->ack; - } else if (s->ttype == MIP_TTYPE_SYN) { - mg_error(c, "Connection timeout"); - } else if (s->ttype == MIP_TTYPE_FIN) { - c->is_closing = 1; - continue; - } else { - if (s->tmiss++ > 2) { - mg_error(c, "keepalive"); - } else { - MG_VERBOSE(("%lu keepalive", c->id)); - tx_tcp(ifp, s->mac, rem_ip, TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq - 1), mg_htonl(s->ack), NULL, 0); - } - } - - settmout(c, MIP_TTYPE_KEEPALIVE); - } - } -} - -// This function executes in interrupt context, thus it should copy data -// somewhere fast. Note that newlib's malloc is not thread safe, thus use -// our lock-free queue with preallocated buffer to copy data and return asap -void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) { - char *p; - if (mg_queue_book(&ifp->recv_queue, &p, len) >= len) { - memcpy(p, buf, len); - mg_queue_add(&ifp->recv_queue, len); - ifp->nrecv++; - } else { - ifp->ndrop++; - } -} - -void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { - // If MAC address is not set, make a random one - if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 && - ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) { - ifp->mac[0] = 0x02; // Locally administered, unicast - mg_random(&ifp->mac[1], sizeof(ifp->mac) - 1); - MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac)); - } - - if (ifp->driver->init && !ifp->driver->init(ifp)) { - MG_ERROR(("driver init failed")); - } else { - size_t framesize = 1540; - ifp->tx.buf = (char *) calloc(1, framesize), ifp->tx.len = framesize; - if (ifp->recv_queue.size == 0) - ifp->recv_queue.size = ifp->driver->rx ? framesize : 8192; - ifp->recv_queue.buf = (char *) calloc(1, ifp->recv_queue.size); - ifp->timer_1000ms = mg_millis(); - mgr->ifp = ifp; - ifp->mgr = mgr; - ifp->mtu = MG_TCPIP_MTU_DEFAULT; - mgr->extraconnsize = sizeof(struct connstate); - if (ifp->ip == 0) ifp->enable_dhcp_client = true; - memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set best-effort to bcast - mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535 - ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from - // MG_EPHEMERAL_PORT_BASE to 65535 - if (ifp->tx.buf == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM")); - } -} - -void mg_tcpip_free(struct mg_tcpip_if *ifp) { - free(ifp->recv_queue.buf); - free(ifp->tx.buf); -} - -static void send_syn(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, - NULL, 0); -} - -static void mac_resolved(struct mg_connection *c) { - if (c->is_udp) { - c->is_connecting = 0; - mg_call(c, MG_EV_CONNECT, NULL); - } else { - send_syn(c); - settmout(c, MIP_TTYPE_SYN); - } -} - -void mg_connect_resolved(struct mg_connection *c) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - c->is_resolving = 0; - if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE; - memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t)); - c->loc.port = mg_htons(ifp->eport++); - MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, - &c->rem)); - mg_call(c, MG_EV_RESOLVE, NULL); - c->is_connecting = 1; - if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) { - struct connstate *s = (struct connstate *) (c + 1); - memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast - mac_resolved(c); - } else if (ifp->ip && ((rem_ip & ifp->mask) == (ifp->ip & ifp->mask)) && - rem_ip != ifp->gw) { // skip if gw (onstatechange -> READY -> ARP) - // If we're in the same LAN, fire an ARP lookup. - MG_DEBUG(("%lu ARP lookup...", c->id)); - mg_tcpip_arp_request(ifp, rem_ip, NULL); - settmout(c, MIP_TTYPE_ARP); - c->is_arplooking = 1; - } else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) { - struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF - uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group - memcpy(s->mac, mcastp, 3); - memcpy(s->mac + 3, ((uint8_t *) &rem_ip) + 1, 3); // 23 LSb - s->mac[3] &= 0x7F; - mac_resolved(c); - } else { - struct connstate *s = (struct connstate *) (c + 1); - memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); - mac_resolved(c); - } -} - -bool mg_open_listener(struct mg_connection *c, const char *url) { - c->loc.port = mg_htons(mg_url_port(url)); - return true; -} - -static void write_conn(struct mg_connection *c) { - long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) - : mg_io_send(c, c->send.buf, c->send.len); - if (len == MG_IO_ERR) { - mg_error(c, "tx err"); - } else if (len > 0) { - mg_iobuf_del(&c->send, 0, (size_t) len); - mg_call(c, MG_EV_WRITE, &len); - } -} - -static void init_closure(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - if (c->is_udp == false && c->is_listening == false && - c->is_connecting == false) { // For TCP conns, - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - tx_tcp(c->mgr->ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, - c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - settmout(c, MIP_TTYPE_FIN); - } -} - -static void close_conn(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - mg_iobuf_free(&s->raw); // For TLS connections, release raw data - mg_close_conn(c); -} - -static bool can_write(struct mg_connection *c) { - return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && - c->is_tls_hs == 0 && c->is_arplooking == 0; -} - -void mg_mgr_poll(struct mg_mgr *mgr, int ms) { - struct mg_connection *c, *tmp; - uint64_t now = mg_millis(); - mg_timer_poll(&mgr->timers, now); - if (mgr->ifp == NULL || mgr->ifp->driver == NULL) return; - mg_tcpip_poll(mgr->ifp, now); - for (c = mgr->conns; c != NULL; c = tmp) { - tmp = c->next; - struct connstate *s = (struct connstate *) (c + 1); - mg_call(c, MG_EV_POLL, &now); - MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't', - c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', - c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); - if (c->is_tls && mg_tls_pending(c) > 0) handle_tls_recv(c); - if (can_write(c)) write_conn(c); - if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) - init_closure(c); - if (c->is_closing) close_conn(c); - } - (void) ms; -} - -bool mg_send(struct mg_connection *c, const void *buf, size_t len) { - struct mg_tcpip_if *ifp = c->mgr->ifp; - bool res = false; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { - mg_error(c, "net down"); - } else if (c->is_udp && (c->is_arplooking || c->is_resolving)) { - // Fail to send, no target MAC or IP - MG_VERBOSE(("still resolving...")); - } else if (c->is_udp) { - struct connstate *s = (struct connstate *) (c + 1); - len = trim_len(c, len); // Trimming length if necessary - tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); - res = true; - } else { - res = mg_iobuf_add(&c->send, c->send.len, buf, len); - } - return res; -} -#endif // MG_ENABLE_TCPIP - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_ch32v307.c" -#endif - - - - -#if MG_OTA == MG_OTA_CH32V307 -// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html - -static bool mg_ch32v307_write(void *, const void *, size_t); -static bool mg_ch32v307_swap(void); - -static struct mg_flash s_mg_flash_ch32v307 = { - (void *) 0x08000000, // Start - 480 * 1024, // Size, first 320k is 0-wait - 4 * 1024, // Sector size, 4k - 4, // Align, 32 bit - mg_ch32v307_write, - mg_ch32v307_swap, -}; - -#define FLASH_BASE 0x40022000 -#define FLASH_ACTLR (FLASH_BASE + 0) -#define FLASH_KEYR (FLASH_BASE + 4) -#define FLASH_OBKEYR (FLASH_BASE + 8) -#define FLASH_STATR (FLASH_BASE + 12) -#define FLASH_CTLR (FLASH_BASE + 16) -#define FLASH_ADDR (FLASH_BASE + 20) -#define FLASH_OBR (FLASH_BASE + 28) -#define FLASH_WPR (FLASH_BASE + 32) - -MG_IRAM static void flash_unlock(void) { - static bool unlocked; - if (unlocked == false) { - MG_REG(FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_KEYR) = 0xcdef89ab; - unlocked = true; - } -} - -MG_IRAM static void flash_wait(void) { - while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0; -} - -MG_IRAM static void mg_ch32v307_erase(void *addr) { - // MG_INFO(("%p", addr)); - flash_unlock(); - flash_wait(); - MG_REG(FLASH_ADDR) = (uint32_t) addr; - MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT; - flash_wait(); -} - -MG_IRAM static bool is_page_boundary(const void *addr) { - uint32_t val = (uint32_t) addr; - return (val & (s_mg_flash_ch32v307.secsz - 1)) == 0; -} - -MG_IRAM static bool mg_ch32v307_write(void *addr, const void *buf, size_t len) { - // MG_INFO(("%p %p %lu", addr, buf, len)); - // mg_hexdump(buf, len); - flash_unlock(); - const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2]; - uint16_t *dst = (uint16_t *) addr; - MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG - // MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR))); - while (src < end) { - if (is_page_boundary(dst)) mg_ch32v307_erase(dst); - *dst++ = *src++; - flash_wait(); - } - MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG - return true; -} - -MG_IRAM bool mg_ch32v307_swap(void) { - return true; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_ch32v307_write(p1 + ofs, p2 + ofs, ss); - } - *((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset() -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_ch32v307); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_ch32v307); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_ch32v307)) { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_ch32v307.size, - s_mg_flash_ch32v307.size / s_mg_flash_ch32v307.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - // TODO() disable IRQ, s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_ch32v307.start, - (char *) s_mg_flash_ch32v307.start + s_mg_flash_ch32v307.size / 2, - s_mg_flash_ch32v307.size / 2, s_mg_flash_ch32v307.secsz); - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_dummy.c" -#endif - - - -#if MG_OTA == MG_OTA_NONE -bool mg_ota_begin(size_t new_firmware_size) { - (void) new_firmware_size; - return true; -} -bool mg_ota_write(const void *buf, size_t len) { - (void) buf, (void) len; - return true; -} -bool mg_ota_end(void) { - return true; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_esp32.c" -#endif - - -#if MG_ARCH == MG_ARCH_ESP32 && MG_OTA == MG_OTA_ESP32 - -static const esp_partition_t *s_ota_update_partition; -static esp_ota_handle_t s_ota_update_handle; -static bool s_ota_success; - -// Those empty macros do nothing, but mark places in the code which could -// potentially trigger a watchdog reboot due to the log flash erase operation -#define disable_wdt() -#define enable_wdt() - -bool mg_ota_begin(size_t new_firmware_size) { - if (s_ota_update_partition != NULL) { - MG_ERROR(("Update in progress. Call mg_ota_end() ?")); - return false; - } else { - s_ota_success = false; - disable_wdt(); - s_ota_update_partition = esp_ota_get_next_update_partition(NULL); - esp_err_t err = esp_ota_begin(s_ota_update_partition, new_firmware_size, - &s_ota_update_handle); - enable_wdt(); - MG_DEBUG(("esp_ota_begin(): %d", err)); - s_ota_success = (err == ESP_OK); - } - return s_ota_success; -} - -bool mg_ota_write(const void *buf, size_t len) { - disable_wdt(); - esp_err_t err = esp_ota_write(s_ota_update_handle, buf, len); - enable_wdt(); - MG_INFO(("esp_ota_write(): %d", err)); - s_ota_success = err == ESP_OK; - return s_ota_success; -} - -bool mg_ota_end(void) { - esp_err_t err = esp_ota_end(s_ota_update_handle); - MG_DEBUG(("esp_ota_end(%p): %d", s_ota_update_handle, err)); - if (s_ota_success && err == ESP_OK) { - err = esp_ota_set_boot_partition(s_ota_update_partition); - s_ota_success = (err == ESP_OK); - } - MG_DEBUG(("Finished ESP32 OTA, success: %d", s_ota_success)); - s_ota_update_partition = NULL; - return s_ota_success; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_imxrt.c" -#endif - - - - -#if MG_OTA >= MG_OTA_RT1020 && MG_OTA <= MG_OTA_RT1170 - -static bool mg_imxrt_write(void *, const void *, size_t); -static bool mg_imxrt_swap(void); - -#if MG_OTA <= MG_OTA_RT1060 -#define MG_IMXRT_FLASH_START 0x60000000 -#define FLEXSPI_NOR_INSTANCE 0 -#elif MG_OTA == MG_OTA_RT1064 -#define MG_IMXRT_FLASH_START 0x70000000 -#define FLEXSPI_NOR_INSTANCE 1 -#else // RT1170 -#define MG_IMXRT_FLASH_START 0x30000000 -#define FLEXSPI_NOR_INSTANCE 1 -#endif - -// TODO(): fill at init, support more devices in a dynamic way -// TODO(): then, check alignment is <= 256, see Wizard's #251 -static struct mg_flash s_mg_flash_imxrt = { - (void *) MG_IMXRT_FLASH_START, // Start, - 4 * 1024 * 1024, // Size, 4mb - 4 * 1024, // Sector size, 4k - 256, // Align, - mg_imxrt_write, - mg_imxrt_swap, -}; - -struct mg_flexspi_lut_seq { - uint8_t seqNum; - uint8_t seqId; - uint16_t reserved; -}; - -struct mg_flexspi_mem_config { - uint32_t tag; - uint32_t version; - uint32_t reserved0; - uint8_t readSampleClkSrc; - uint8_t csHoldTime; - uint8_t csSetupTime; - uint8_t columnAddressWidth; - uint8_t deviceModeCfgEnable; - uint8_t deviceModeType; - uint16_t waitTimeCfgCommands; - struct mg_flexspi_lut_seq deviceModeSeq; - uint32_t deviceModeArg; - uint8_t configCmdEnable; - uint8_t configModeType[3]; - struct mg_flexspi_lut_seq configCmdSeqs[3]; - uint32_t reserved1; - uint32_t configCmdArgs[3]; - uint32_t reserved2; - uint32_t controllerMiscOption; - uint8_t deviceType; - uint8_t sflashPadType; - uint8_t serialClkFreq; - uint8_t lutCustomSeqEnable; - uint32_t reserved3[2]; - uint32_t sflashA1Size; - uint32_t sflashA2Size; - uint32_t sflashB1Size; - uint32_t sflashB2Size; - uint32_t csPadSettingOverride; - uint32_t sclkPadSettingOverride; - uint32_t dataPadSettingOverride; - uint32_t dqsPadSettingOverride; - uint32_t timeoutInMs; - uint32_t commandInterval; - uint16_t dataValidTime[2]; - uint16_t busyOffset; - uint16_t busyBitPolarity; - uint32_t lookupTable[64]; - struct mg_flexspi_lut_seq lutCustomSeq[12]; - uint32_t reserved4[4]; -}; - -struct mg_flexspi_nor_config { - struct mg_flexspi_mem_config memConfig; - uint32_t pageSize; - uint32_t sectorSize; - uint8_t ipcmdSerialClkFreq; - uint8_t isUniformBlockSize; - uint8_t reserved0[2]; - uint8_t serialNorType; - uint8_t needExitNoCmdMode; - uint8_t halfClkForNonReadCmd; - uint8_t needRestoreNoCmdMode; - uint32_t blockSize; - uint32_t reserve2[11]; -}; - -/* FLEXSPI memory config block related defintions */ -#define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian -#define MG_FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0 - -#define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \ - (MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | \ - MG_FLEXSPI_LUT_OPCODE0(cmd0) | MG_FLEXSPI_LUT_OPERAND1(op1) | \ - MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1)) - -#define MG_CMD_SDR 0x01 -#define MG_CMD_DDR 0x21 -#define MG_DUMMY_SDR 0x0C -#define MG_DUMMY_DDR 0x2C -#define MG_RADDR_SDR 0x02 -#define MG_RADDR_DDR 0x22 -#define MG_READ_SDR 0x09 -#define MG_READ_DDR 0x29 -#define MG_WRITE_SDR 0x08 -#define MG_WRITE_DDR 0x28 -#define MG_STOP 0 - -#define MG_FLEXSPI_1PAD 0 -#define MG_FLEXSPI_2PAD 1 -#define MG_FLEXSPI_4PAD 2 -#define MG_FLEXSPI_8PAD 3 - -#define MG_FLEXSPI_QSPI_LUT \ - { \ - [0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB, MG_RADDR_SDR, \ - MG_FLEXSPI_4PAD, 0x18), \ - [1] = MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06, MG_READ_SDR, \ - MG_FLEXSPI_4PAD, 0x04), \ - [4 * 1 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05, \ - MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04), \ - [4 * 3 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - [4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20, \ - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \ - [4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xD8, \ - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \ - [4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02, \ - MG_RADDR_SDR, MG_FLEXSPI_1PAD, 0x18), \ - [4 * 9 + 1] = MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x04, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - [4 * 11 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60, \ - MG_STOP, MG_FLEXSPI_1PAD, 0x0), \ - } - -#define MG_FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU) -#define MG_FLEXSPI_LUT_NUM_PADS0(x) \ - (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U) -#define MG_FLEXSPI_LUT_OPCODE0(x) \ - (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U) -#define MG_FLEXSPI_LUT_OPERAND1(x) \ - (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U) -#define MG_FLEXSPI_LUT_NUM_PADS1(x) \ - (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U) -#define MG_FLEXSPI_LUT_OPCODE1(x) \ - (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U) - -#if MG_OTA == MG_OTA_RT1020 -// RT102X boards support ROM API version 1.4 -struct mg_flexspi_nor_driver_interface { - uint32_t version; - int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t dst_addr, const uint32_t *src); - uint32_t reserved; - int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t lengthInBytes); - uint32_t reserved2; - int (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t seqNumber); - int (*xfer)(uint32_t instance, char *xfer); - void (*clear_cache)(uint32_t instance); -}; -#elif MG_OTA <= MG_OTA_RT1064 -// RT104x and RT106x support ROM API version 1.5 -struct mg_flexspi_nor_driver_interface { - uint32_t version; - int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t dst_addr, const uint32_t *src); - int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t lengthInBytes); - int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *dst, uint32_t addr, uint32_t lengthInBytes); - void (*clear_cache)(uint32_t instance); - int (*xfer)(uint32_t instance, char *xfer); - int (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t seqNumber); - int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *option); -}; -#else -// RT117x support ROM API version 1.7 -struct mg_flexspi_nor_driver_interface { - uint32_t version; - int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t dst_addr, const uint32_t *src); - int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config); - int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t start, uint32_t lengthInBytes); - int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *dst, uint32_t addr, uint32_t lengthInBytes); - uint32_t reserved; - int (*xfer)(uint32_t instance, char *xfer); - int (*update_lut)(uint32_t instance, uint32_t seqIndex, - const uint32_t *lutBase, uint32_t seqNumber); - int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t *option); - int (*erase_sector)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t address); - int (*erase_block)(uint32_t instance, struct mg_flexspi_nor_config *config, - uint32_t address); - void (*hw_reset)(uint32_t instance, uint32_t resetLogic); - int (*wait_busy)(uint32_t instance, struct mg_flexspi_nor_config *config, - bool isParallelMode, uint32_t address); - int (*set_clock_source)(uint32_t instance, uint32_t clockSrc); - void (*config_clock)(uint32_t instance, uint32_t freqOption, - uint32_t sampleClkMode); -}; -#endif - -#if MG_OTA <= MG_OTA_RT1064 -#define MG_FLEXSPI_BASE 0x402A8000 -#define flexspi_nor \ - (*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0020001c + \ - 16))) -#else -#define MG_FLEXSPI_BASE 0x400CC000 -#define flexspi_nor \ - (*((struct mg_flexspi_nor_driver_interface **) (*(uint32_t *) 0x0021001c + \ - 12))) -#endif - -static bool s_flash_irq_disabled; - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_imxrt.start, *end = base + s_mg_flash_imxrt.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_imxrt.secsz) == 0; -} - -// Note: the get_config function below works both for RT1020 and 1060 -// must reside in RAM, as flash will be erased -static struct mg_flexspi_nor_config default_config = { - .memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG, - .version = MG_FLEXSPI_CFG_BLK_VERSION, - .readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad - .csHoldTime = 3, - .csSetupTime = 3, - .controllerMiscOption = MG_BIT(4), - .deviceType = 1, // serial NOR - .sflashPadType = 4, - .serialClkFreq = 7, // 133MHz - .sflashA1Size = 8 * 1024 * 1024, - .lookupTable = MG_FLEXSPI_QSPI_LUT}, - .pageSize = 256, - .sectorSize = 4 * 1024, - .ipcmdSerialClkFreq = 1, - .blockSize = 64 * 1024, - .isUniformBlockSize = false -}; -MG_IRAM static int flexspi_nor_get_config( - struct mg_flexspi_nor_config **config) { - *config = &default_config; - return 0; -} - -#if 0 -// ROM API get_config call (ROM version >= 1.5) -MG_IRAM static int flexspi_nor_get_config( - struct mg_flexspi_nor_config **config) { - uint32_t options[] = {0xc0000000, 0x00}; - - MG_ARM_DISABLE_IRQ(); - uint32_t status = - flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, *config, options); - if (!s_flash_irq_disabled) { - MG_ARM_ENABLE_IRQ(); - } - if (status) { - MG_ERROR(("Failed to extract flash configuration: status %u", status)); - } - return status; -} -#endif - -MG_IRAM static void mg_spin(volatile uint32_t count) { - while (count--) (void) 0; -} - -MG_IRAM static void flash_wait(void) { - while ((*((volatile uint32_t *) (MG_FLEXSPI_BASE + 0xE0)) & MG_BIT(1)) == 0) - mg_spin(1); -} - -MG_IRAM static bool flash_erase(struct mg_flexspi_nor_config *config, - void *addr) { - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - - void *dst = (void *) ((char *) addr - (char *) s_mg_flash_imxrt.start); - - bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, config, (uint32_t) dst, - s_mg_flash_imxrt.secsz) == 0); - MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); - return ok; -} - -#if 0 -// standalone erase call -MG_IRAM static bool mg_imxrt_erase(void *addr) { - struct mg_flexspi_nor_config config, *config_ptr = &config; - bool ret; - // Interrupts must be disabled before calls to ROM API in RT1020 and 1060 - MG_ARM_DISABLE_IRQ(); - ret = (flexspi_nor_get_config(&config_ptr) == 0); - if (ret) ret = flash_erase(config_ptr, addr); - MG_ARM_ENABLE_IRQ(); - return ret; -} -#endif - -MG_IRAM bool mg_imxrt_swap(void) { - return true; -} - -MG_IRAM static bool mg_imxrt_write(void *addr, const void *buf, size_t len) { - struct mg_flexspi_nor_config config, *config_ptr = &config; - bool ok = false; - // Interrupts must be disabled before calls to ROM API in RT1020 and 1060 - MG_ARM_DISABLE_IRQ(); - if (flexspi_nor_get_config(&config_ptr) != 0) goto fwxit; - if ((len % s_mg_flash_imxrt.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_imxrt.align)); - goto fwxit; - } - if ((char *) addr < (char *) s_mg_flash_imxrt.start) { - MG_ERROR(("Invalid flash write address: %p", addr)); - goto fwxit; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - ok = true; - - while (ok && src < end) { - if (flash_page_start(dst) && flash_erase(config_ptr, dst) == false) { - ok = false; - break; - } - uint32_t status; - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_imxrt.start; - if ((char *) buf >= (char *) s_mg_flash_imxrt.start) { - // If we copy from FLASH to FLASH, then we first need to copy the source - // to RAM - size_t tmp_buf_size = s_mg_flash_imxrt.align / sizeof(uint32_t); - uint32_t tmp[tmp_buf_size]; - - for (size_t i = 0; i < tmp_buf_size; i++) { - flash_wait(); - tmp[i] = src[i]; - } - status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr, - (uint32_t) dst_ofs, tmp); - } else { - status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, config_ptr, - (uint32_t) dst_ofs, src); - } - src = (uint32_t *) ((char *) src + s_mg_flash_imxrt.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_imxrt.align); - if (status != 0) { - ok = false; - } - } - MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); -fwxit: - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - return ok; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_imxrt_write(p1 + ofs, p2 + ofs, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_imxrt); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_imxrt); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_imxrt)) { - if (0) { // is_dualbank() - // TODO(): no devices so far - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_imxrt.size, - s_mg_flash_imxrt.size / s_mg_flash_imxrt.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_imxrt.start, - (char *) s_mg_flash_imxrt.start + s_mg_flash_imxrt.size / 2, - s_mg_flash_imxrt.size / 2, s_mg_flash_imxrt.secsz); - } - } - return false; -} - -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_mcxn.c" -#endif - - - - -#if MG_OTA == MG_OTA_MCXN - -// - Flash phrase: 16 bytes; smallest portion programmed in one operation. -// - Flash page: 128 bytes; largest portion programmed in one operation. -// - Flash sector: 8 KB; smallest portion that can be erased in one operation. -// - Flash API mg_flash_driver->program: "start" and "len" must be page-size -// aligned; to use 'phrase', FMU register access is needed. Using ROM - -static bool mg_mcxn_write(void *, const void *, size_t); -static bool mg_mcxn_swap(void); - -static struct mg_flash s_mg_flash_mcxn = { - (void *) 0, // Start, filled at init - 0, // Size, filled at init - 0, // Sector size, filled at init - 0, // Align, filled at init - mg_mcxn_write, - mg_mcxn_swap, -}; - -struct mg_flash_config { - uint32_t addr; - uint32_t size; - uint32_t blocks; - uint32_t page_size; - uint32_t sector_size; - uint32_t ffr[6]; - uint32_t reserved0[5]; - uint32_t *bootctx; - bool useahb; -}; - -struct mg_flash_driver_interface { - uint32_t version; - uint32_t (*init)(struct mg_flash_config *); - uint32_t (*erase)(struct mg_flash_config *, uint32_t start, uint32_t len, - uint32_t key); - uint32_t (*program)(struct mg_flash_config *, uint32_t start, uint8_t *src, - uint32_t len); - uint32_t (*verify_erase)(struct mg_flash_config *, uint32_t start, - uint32_t len); - uint32_t (*verify_program)(struct mg_flash_config *, uint32_t start, - uint32_t len, const uint8_t *expected, - uint32_t *addr, uint32_t *failed); - uint32_t reserved1[12]; - uint32_t (*read)(struct mg_flash_config *, uint32_t start, uint8_t *dest, - uint32_t len); - uint32_t reserved2[4]; - uint32_t (*deinit)(struct mg_flash_config *); -}; -#define mg_flash_driver \ - ((struct mg_flash_driver_interface *) (*((uint32_t *) 0x1303fc00 + 4))) -#define MG_MCXN_FLASK_KEY (('k' << 24) | ('e' << 16) | ('f' << 8) | 'l') - -MG_IRAM static bool flash_sector_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_mcxn.start, - *end = base + s_mg_flash_mcxn.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_mcxn.secsz) == 0; -} - -MG_IRAM static bool flash_erase(struct mg_flash_config *config, void *addr) { - if (flash_sector_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - uint32_t dst = - (uint32_t) addr - (uint32_t) s_mg_flash_mcxn.start; // future-proof - uint32_t status = mg_flash_driver->erase(config, dst, s_mg_flash_mcxn.secsz, - MG_MCXN_FLASK_KEY); - bool ok = (status == 0); - if (!ok) MG_ERROR(("Flash write error: %lu", status)); - MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail")); - return ok; -} - -#if 0 -// read-while-write, no need to disable IRQs for standalone usage -MG_IRAM static bool mg_mcxn_erase(void *addr) { - uint32_t status; - struct mg_flash_config config; - if ((status = mg_flash_driver->init(&config)) != 0) { - MG_ERROR(("Flash driver init error: %lu", status)); - return false; - } - bool ok = flash_erase(&config, addr); - mg_flash_driver->deinit(&config); - return ok; -} -#endif - -MG_IRAM static bool mg_mcxn_swap(void) { - // TODO(): no devices so far - return true; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_mcxn_write(void *addr, const void *buf, size_t len) { - bool ok = false; - uint32_t status; - struct mg_flash_config config; - if ((status = mg_flash_driver->init(&config)) != 0) { - MG_ERROR(("Flash driver init error: %lu", status)); - return false; - } - if ((len % s_mg_flash_mcxn.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_mcxn.align)); - goto fwxit; - } - if ((((size_t) addr - (size_t) s_mg_flash_mcxn.start) % - s_mg_flash_mcxn.align) != 0) { - MG_ERROR(("%p is not on a page boundary", addr)); - goto fwxit; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - ok = true; - - MG_ARM_DISABLE_IRQ(); - while (ok && src < end) { - if (flash_sector_start(dst) && flash_erase(&config, dst) == false) { - ok = false; - break; - } - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_mcxn.start; - // assume source is in RAM or in a different bank or read-while-write - status = mg_flash_driver->program(&config, dst_ofs, (uint8_t *) src, - s_mg_flash_mcxn.align); - src = (uint32_t *) ((char *) src + s_mg_flash_mcxn.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_mcxn.align); - if (status != 0) { - MG_ERROR(("Flash write error: %lu", status)); - ok = false; - } - } - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail")); - -fwxit: - mg_flash_driver->deinit(&config); - return ok; -} - -// try to swap (honor dual image), otherwise just overwrite -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - char *tmp = malloc(ss); - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - if (tmp != NULL) - for (size_t i = 0; i < ss; i++) tmp[i] = p1[ofs + i]; - mg_mcxn_write(p1 + ofs, p2 + ofs, ss); - if (tmp != NULL) mg_mcxn_write(p2 + ofs, tmp, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - uint32_t status; - struct mg_flash_config config; - if ((status = mg_flash_driver->init(&config)) != 0) { - MG_ERROR(("Flash driver init error: %lu", status)); - return false; - } - s_mg_flash_mcxn.start = (void *) config.addr; - s_mg_flash_mcxn.size = config.size; - s_mg_flash_mcxn.secsz = config.sector_size; - s_mg_flash_mcxn.align = config.page_size; - mg_flash_driver->deinit(&config); - MG_DEBUG( - ("%lu-byte flash @%p, using %lu-byte sectors with %lu-byte-aligned pages", - s_mg_flash_mcxn.size, s_mg_flash_mcxn.start, s_mg_flash_mcxn.secsz, - s_mg_flash_mcxn.align)); - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_mcxn); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_mcxn); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_mcxn)) { - if (0) { // is_dualbank() - // TODO(): no devices so far - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_mcxn.size, - s_mg_flash_mcxn.size / s_mg_flash_mcxn.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_mcxn.start, - (char *) s_mg_flash_mcxn.start + s_mg_flash_mcxn.size / 2, - s_mg_flash_mcxn.size / 2, s_mg_flash_mcxn.secsz); - } - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_picosdk.c" -#endif - - - - -#if MG_OTA == MG_OTA_PICOSDK - -// Both RP2040 and RP2350 have no flash, low-level flash access support in -// bootrom, and high-level support in Pico-SDK (2.0+ for the RP2350) -// - The RP2350 in RISC-V mode is not yet (fully) supported (nor tested) - -static bool mg_picosdk_write(void *, const void *, size_t); -static bool mg_picosdk_swap(void); - -static struct mg_flash s_mg_flash_picosdk = { - (void *) 0x10000000, // Start, not used here; functions handle offset -#ifdef PICO_FLASH_SIZE_BYTES - PICO_FLASH_SIZE_BYTES, // Size, from board definitions -#else - 0x200000, // Size, guess... is 2M enough ? -#endif - FLASH_SECTOR_SIZE, // Sector size, from hardware_flash - FLASH_PAGE_SIZE, // Align, from hardware_flash - mg_picosdk_write, mg_picosdk_swap, -}; - -#define MG_MODULO2(x, m) ((x) & ((m) -1)) - -static bool __no_inline_not_in_flash_func(flash_sector_start)( - volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_picosdk.start, - *end = base + s_mg_flash_picosdk.size; - volatile char *p = (char *) dst; - return p >= base && p < end && - MG_MODULO2(p - base, s_mg_flash_picosdk.secsz) == 0; -} - -static bool __no_inline_not_in_flash_func(flash_erase)(void *addr) { - if (flash_sector_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - return false; - } - void *dst = (void *) ((char *) addr - (char *) s_mg_flash_picosdk.start); - flash_range_erase((uint32_t) dst, s_mg_flash_picosdk.secsz); - MG_DEBUG(("Sector starting at %p erasure", addr)); - return true; -} - -static bool __no_inline_not_in_flash_func(mg_picosdk_swap)(void) { - // TODO(): RP2350 might have some A/B functionality (DS 5.1) - return true; -} - -static bool s_flash_irq_disabled; - -static bool __no_inline_not_in_flash_func(mg_picosdk_write)(void *addr, - const void *buf, - size_t len) { - if ((len % s_mg_flash_picosdk.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_picosdk.align)); - return false; - } - if ((((size_t) addr - (size_t) s_mg_flash_picosdk.start) % - s_mg_flash_picosdk.align) != 0) { - MG_ERROR(("%p is not on a page boundary", addr)); - return false; - } - - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - -#ifndef __riscv - MG_ARM_DISABLE_IRQ(); -#else - asm volatile("csrrc zero, mstatus, %0" : : "i"(1 << 3) : "memory"); -#endif - while (src < end) { - uint32_t dst_ofs = (uint32_t) dst - (uint32_t) s_mg_flash_picosdk.start; - if (flash_sector_start(dst) && flash_erase(dst) == false) break; - // flash_range_program() runs in RAM and handles writing up to - // FLASH_PAGE_SIZE bytes. Source must not be in flash - flash_range_program((uint32_t) dst_ofs, (uint8_t *) src, - s_mg_flash_picosdk.align); - src = (uint32_t *) ((char *) src + s_mg_flash_picosdk.align); - dst = (uint32_t *) ((char *) dst + s_mg_flash_picosdk.align); - } - if (!s_flash_irq_disabled) { -#ifndef __riscv - MG_ARM_ENABLE_IRQ(); -#else - asm volatile("csrrs mstatus, %0" : : "i"(1 << 3) : "memory"); -#endif - } - MG_DEBUG(("Flash write %lu bytes @ %p.", len, dst)); - return true; -} - -// just overwrite instead of swap -static void __no_inline_not_in_flash_func(single_bank_swap)(char *p1, char *p2, - size_t s, - size_t ss) { - char *tmp = malloc(ss); - if (tmp == NULL) return; -#if PICO_RP2040 - uint32_t xip[256 / sizeof(uint32_t)]; - void *dst = (void *) ((char *) p1 - (char *) s_mg_flash_picosdk.start); - size_t count = MG_ROUND_UP(s, ss); - // use SDK function calls to get BootROM function pointers - rom_connect_internal_flash_fn connect = (rom_connect_internal_flash_fn) rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); - rom_flash_exit_xip_fn xit = (rom_flash_exit_xip_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); - rom_flash_range_program_fn program = (rom_flash_range_program_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_RANGE_PROGRAM); - rom_flash_flush_cache_fn flush = (rom_flash_flush_cache_fn) rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); - // no stdlib calls here. - MG_ARM_DISABLE_IRQ(); - // 2nd bootloader (XIP) is in flash, SDK functions copy it to RAM on entry - for (size_t i = 0; i < 256 / sizeof(uint32_t); i++) - xip[i] = ((uint32_t *) (s_mg_flash_picosdk.start))[i]; - flash_range_erase((uint32_t) dst, count); - // flash has been erased, no XIP to copy. Only BootROM calls possible - for (uint32_t ofs = 0; ofs < s; ofs += ss) { - for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i]; - __compiler_memory_barrier(); - connect(); - xit(); - program((uint32_t) dst + ofs, tmp, ss); - flush(); - ((void (*)(void))((intptr_t) xip + 1))(); // enter XIP again - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; // AIRCR = SYSRESETREQ -#else - // RP2350 has bootram and copies second bootloader there, SDK uses that copy, - // It might also be able to take advantage of partition swapping - for (size_t ofs = 0; ofs < s; ofs += ss) { - for (size_t i = 0; i < ss; i++) tmp[i] = p2[ofs + i]; - mg_picosdk_write(p1 + ofs, tmp, ss); - } -#ifndef __riscv - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; // AIRCR = SYSRESETREQ -#else - // TODO(): find a way to do a system reset, like block resets and watchdog -#endif -#endif -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_picosdk); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_picosdk); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_picosdk)) { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_picosdk.size, - s_mg_flash_picosdk.size / s_mg_flash_picosdk.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished or return on failure - single_bank_swap( - (char *) s_mg_flash_picosdk.start, - (char *) s_mg_flash_picosdk.start + s_mg_flash_picosdk.size / 2, - s_mg_flash_picosdk.size / 2, s_mg_flash_picosdk.secsz); - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_stm32f.c" -#endif - - - - -#if MG_OTA == MG_OTA_STM32F - -static bool mg_stm32f_write(void *, const void *, size_t); -static bool mg_stm32f_swap(void); - -static struct mg_flash s_mg_flash_stm32f = { - (void *) 0x08000000, // Start - 0, // Size, FLASH_SIZE_REG - 0, // Irregular sector size - 32, // Align, 256 bit - mg_stm32f_write, - mg_stm32f_swap, -}; - -#define MG_FLASH_BASE 0x40023c00 -#define MG_FLASH_KEYR 0x04 -#define MG_FLASH_SR 0x0c -#define MG_FLASH_CR 0x10 -#define MG_FLASH_OPTCR 0x14 -#define MG_FLASH_SIZE_REG_F7 0x1FF0F442 -#define MG_FLASH_SIZE_REG_F4 0x1FFF7A22 - -#define STM_DBGMCU_IDCODE 0xE0042000 -#define STM_DEV_ID (MG_REG(STM_DBGMCU_IDCODE) & (MG_BIT(12) - 1)) -#define SYSCFG_MEMRMP 0x40013800 - -#define MG_FLASH_SIZE_REG_LOCATION \ - ((STM_DEV_ID >= 0x449) ? MG_FLASH_SIZE_REG_F7 : MG_FLASH_SIZE_REG_F4) - -static size_t flash_size(void) { - return (MG_REG(MG_FLASH_SIZE_REG_LOCATION) & 0xFFFF) * 1024; -} - -MG_IRAM static int is_dualbank(void) { - // only F42x/F43x series (0x419) support dual bank - return STM_DEV_ID == 0x419; -} - -MG_IRAM static void flash_unlock(void) { - static bool unlocked = false; - if (unlocked == false) { - MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0x45670123; - MG_REG(MG_FLASH_BASE + MG_FLASH_KEYR) = 0xcdef89ab; - unlocked = true; - } -} - -#define MG_FLASH_CONFIG_16_64_128 1 // used by STM32F7 -#define MG_FLASH_CONFIG_32_128_256 2 // used by STM32F4 and F2 - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_stm32f.start; - char *end = base + s_mg_flash_stm32f.size; - - if (is_dualbank() && dst >= (uint32_t *) (base + (end - base) / 2)) { - dst = (uint32_t *) ((uint32_t) dst - (end - base) / 2); - } - - uint32_t flash_config = MG_FLASH_CONFIG_16_64_128; - if (STM_DEV_ID >= 0x449) { - flash_config = MG_FLASH_CONFIG_32_128_256; - } - - volatile char *p = (char *) dst; - if (p >= base && p < end) { - if (p < base + 16 * 1024 * 4 * flash_config) { - if ((p - base) % (16 * 1024 * flash_config) == 0) return true; - } else if (p == base + 16 * 1024 * 4 * flash_config) { - return true; - } else if ((p - base) % (128 * 1024 * flash_config) == 0) { - return true; - } - } - return false; -} - -MG_IRAM static int flash_sector(volatile uint32_t *addr) { - char *base = (char *) s_mg_flash_stm32f.start; - char *end = base + s_mg_flash_stm32f.size; - bool addr_in_bank_2 = false; - if (is_dualbank() && addr >= (uint32_t *) (base + (end - base) / 2)) { - addr = (uint32_t *) ((uint32_t) addr - (end - base) / 2); - addr_in_bank_2 = true; - } - volatile char *p = (char *) addr; - uint32_t flash_config = MG_FLASH_CONFIG_16_64_128; - if (STM_DEV_ID >= 0x449) { - flash_config = MG_FLASH_CONFIG_32_128_256; - } - int sector = -1; - if (p >= base && p < end) { - if (p < base + 16 * 1024 * 4 * flash_config) { - sector = (p - base) / (16 * 1024 * flash_config); - } else if (p >= base + 64 * 1024 * flash_config && - p < base + 128 * 1024 * flash_config) { - sector = 4; - } else { - sector = (p - base) / (128 * 1024 * flash_config) + 4; - } - } - if (sector == -1) return -1; - if (addr_in_bank_2) sector += 12; // a bank has 12 sectors - return sector; -} - -MG_IRAM static bool flash_is_err(void) { - return MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & ((MG_BIT(7) - 1) << 1); -} - -MG_IRAM static void flash_wait(void) { - while (MG_REG(MG_FLASH_BASE + MG_FLASH_SR) & (MG_BIT(16))) (void) 0; -} - -MG_IRAM static void flash_clear_err(void) { - flash_wait(); // Wait until ready - MG_REG(MG_FLASH_BASE + MG_FLASH_SR) = 0xf2; // Clear all errors -} - -MG_IRAM static bool mg_stm32f_erase(void *addr) { - bool ok = false; - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - } else { - int sector = flash_sector(addr); - if (sector < 0) return false; - uint32_t sector_reg = sector; - if (is_dualbank() && sector >= 12) { - // 3.9.8 Flash control register (FLASH_CR) for F42xxx and F43xxx - // BITS[7:3] - sector_reg -= 12; - sector_reg |= MG_BIT(4); - } - flash_unlock(); - flash_wait(); - uint32_t cr = MG_BIT(1); // SER - cr |= MG_BIT(16); // STRT - cr |= (sector_reg & 31) << 3; // sector - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = cr; - ok = !flash_is_err(); - MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr, - ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR), - MG_REG(MG_FLASH_BASE + MG_FLASH_SR))); - // After we have erased the sector, set CR flags for programming - // 2 << 8 is word write parallelism, bit(0) is PG. RM0385, section 3.7.5 - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | (2 << 8); - flash_clear_err(); - } - return ok; -} - -MG_IRAM static bool mg_stm32f_swap(void) { - // STM32 F42x/F43x support dual bank, however, the memory mapping - // change will not be carried through a hard reset. Therefore, we will use - // the single bank approach for this family as well. - return true; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_stm32f_write(void *addr, const void *buf, size_t len) { - if ((len % s_mg_flash_stm32f.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32f.align)); - return false; - } - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - bool ok = true; - MG_ARM_DISABLE_IRQ(); - flash_unlock(); - flash_clear_err(); - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) = MG_BIT(0) | MG_BIT(9); // PG, 32-bit - flash_wait(); - MG_DEBUG(("Writing flash @ %p, %lu bytes", addr, len)); - while (ok && src < end) { - if (flash_page_start(dst) && mg_stm32f_erase(dst) == false) break; - *(volatile uint32_t *) dst++ = *src++; - MG_DSB(); // ensure flash is written with no errors - flash_wait(); - if (flash_is_err()) ok = false; - } - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, - ok ? "ok" : "fail", MG_REG(MG_FLASH_BASE + MG_FLASH_CR), - MG_REG(MG_FLASH_BASE + MG_FLASH_SR))); - MG_REG(MG_FLASH_BASE + MG_FLASH_CR) &= ~MG_BIT(0); // Clear programming flag - return ok; -} - -// just overwrite instead of swap -MG_IRAM void single_bank_swap(char *p1, char *p2, size_t size) { - // no stdlib calls here - mg_stm32f_write(p1, p2, size); - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - s_mg_flash_stm32f.size = flash_size(); - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32f); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_stm32f); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_stm32f)) { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_stm32f.size, STM_DEV_ID == 0x449 ? 8 : 12)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - char *p1 = (char *) s_mg_flash_stm32f.start; - char *p2 = p1 + s_mg_flash_stm32f.size / 2; - size_t size = s_mg_flash_stm32f.size / 2; - // Runs in RAM, will reset when finished - single_bank_swap(p1, p2, size); - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_stm32h5.c" -#endif - - - - -#if MG_OTA == MG_OTA_STM32H5 - -static bool mg_stm32h5_write(void *, const void *, size_t); -static bool mg_stm32h5_swap(void); - -static struct mg_flash s_mg_flash_stm32h5 = { - (void *) 0x08000000, // Start - 2 * 1024 * 1024, // Size, 2Mb - 8 * 1024, // Sector size, 8k - 16, // Align, 128 bit - mg_stm32h5_write, - mg_stm32h5_swap, -}; - -#define MG_FLASH_BASE 0x40022000 // Base address of the flash controller -#define FLASH_KEYR (MG_FLASH_BASE + 0x4) // See RM0481 7.11 -#define FLASH_OPTKEYR (MG_FLASH_BASE + 0xc) -#define FLASH_OPTCR (MG_FLASH_BASE + 0x1c) -#define FLASH_NSSR (MG_FLASH_BASE + 0x20) -#define FLASH_NSCR (MG_FLASH_BASE + 0x28) -#define FLASH_NSCCR (MG_FLASH_BASE + 0x30) -#define FLASH_OPTSR_CUR (MG_FLASH_BASE + 0x50) -#define FLASH_OPTSR_PRG (MG_FLASH_BASE + 0x54) - -static void flash_unlock(void) { - static bool unlocked = false; - if (unlocked == false) { - MG_REG(FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_KEYR) = 0Xcdef89ab; - MG_REG(FLASH_OPTKEYR) = 0x08192a3b; - MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f; - unlocked = true; - } -} - -static int flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_stm32h5.start, - *end = base + s_mg_flash_stm32h5.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_stm32h5.secsz) == 0; -} - -static bool flash_is_err(void) { - return MG_REG(FLASH_NSSR) & ((MG_BIT(8) - 1) << 17); // RM0481 7.11.9 -} - -static void flash_wait(void) { - while ((MG_REG(FLASH_NSSR) & MG_BIT(0)) && - (MG_REG(FLASH_NSSR) & MG_BIT(16)) == 0) { - (void) 0; - } -} - -static void flash_clear_err(void) { - flash_wait(); // Wait until ready - MG_REG(FLASH_NSCCR) = ((MG_BIT(9) - 1) << 16U); // Clear all errors -} - -static bool flash_bank_is_swapped(void) { - return MG_REG(FLASH_OPTCR) & MG_BIT(31); // RM0481 7.11.8 -} - -static bool mg_stm32h5_erase(void *location) { - bool ok = false; - if (flash_page_start(location) == false) { - MG_ERROR(("%p is not on a sector boundary")); - } else { - uintptr_t diff = (char *) location - (char *) s_mg_flash_stm32h5.start; - uint32_t sector = diff / s_mg_flash_stm32h5.secsz; - uint32_t saved_cr = MG_REG(FLASH_NSCR); // Save CR value - flash_unlock(); - flash_clear_err(); - MG_REG(FLASH_NSCR) = 0; - if ((sector < 128 && flash_bank_is_swapped()) || - (sector > 127 && !flash_bank_is_swapped())) { - MG_REG(FLASH_NSCR) |= MG_BIT(31); // Set FLASH_CR_BKSEL - } - if (sector > 127) sector -= 128; - MG_REG(FLASH_NSCR) |= MG_BIT(2) | (sector << 6); // Erase | sector_num - MG_REG(FLASH_NSCR) |= MG_BIT(5); // Start erasing - flash_wait(); - ok = !flash_is_err(); - MG_DEBUG(("Erase sector %lu @ %p: %s. CR %#lx SR %#lx", sector, location, - ok ? "ok" : "fail", MG_REG(FLASH_NSCR), MG_REG(FLASH_NSSR))); - // mg_hexdump(location, 32); - MG_REG(FLASH_NSCR) = saved_cr; // Restore saved CR - } - return ok; -} - -static bool mg_stm32h5_swap(void) { - uint32_t desired = flash_bank_is_swapped() ? 0 : MG_BIT(31); - flash_unlock(); - flash_clear_err(); - // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG); - MG_SET_BITS(MG_REG(FLASH_OPTSR_PRG), MG_BIT(31), desired); - // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG); - MG_REG(FLASH_OPTCR) |= MG_BIT(1); // OPTSTART - while ((MG_REG(FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0; - return true; -} - -static bool mg_stm32h5_write(void *addr, const void *buf, size_t len) { - if ((len % s_mg_flash_stm32h5.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32h5.align)); - return false; - } - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - bool ok = true; - MG_ARM_DISABLE_IRQ(); - flash_unlock(); - flash_clear_err(); - MG_REG(FLASH_NSCR) = MG_BIT(1); // Set programming flag - while (ok && src < end) { - if (flash_page_start(dst) && mg_stm32h5_erase(dst) == false) { - ok = false; - break; - } - *(volatile uint32_t *) dst++ = *src++; - flash_wait(); - if (flash_is_err()) ok = false; - } - MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, - flash_is_err() ? "fail" : "ok", MG_REG(FLASH_NSCR), - MG_REG(FLASH_NSSR))); - MG_REG(FLASH_NSCR) = 0; // Clear flags - return ok; -} - -bool mg_ota_begin(size_t new_firmware_size) { - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32h5); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_stm32h5); -} - -// Actual bank swap is deferred until reset, it is safe to execute in flash -bool mg_ota_end(void) { - if(!mg_ota_flash_end(&s_mg_flash_stm32h5)) return false; - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - return true; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ota_stm32h7.c" -#endif - - - - -#if MG_OTA == MG_OTA_STM32H7 || MG_OTA == MG_OTA_STM32H7_DUAL_CORE - -// - H723/735 RM 4.3.3: Note: The application can simultaneously request a read -// and a write operation through the AXI interface. -// - We only need IRAM for partition swapping in the H723, however, all -// related functions must reside in IRAM for this to be possible. -// - Linker files for other devices won't define a .iram section so there's no -// associated penalty - -static bool mg_stm32h7_write(void *, const void *, size_t); -static bool mg_stm32h7_swap(void); - -static struct mg_flash s_mg_flash_stm32h7 = { - (void *) 0x08000000, // Start - 0, // Size, FLASH_SIZE_REG - 128 * 1024, // Sector size, 128k - 32, // Align, 256 bit - mg_stm32h7_write, - mg_stm32h7_swap, -}; - -#define FLASH_BASE1 0x52002000 // Base address for bank1 -#define FLASH_BASE2 0x52002100 // Base address for bank2 -#define FLASH_KEYR 0x04 // See RM0433 4.9.2 -#define FLASH_OPTKEYR 0x08 -#define FLASH_OPTCR 0x18 -#define FLASH_SR 0x10 -#define FLASH_CR 0x0c -#define FLASH_CCR 0x14 -#define FLASH_OPTSR_CUR 0x1c -#define FLASH_OPTSR_PRG 0x20 -#define FLASH_SIZE_REG 0x1ff1e880 - -#define IS_DUALCORE() (MG_OTA == MG_OTA_STM32H7_DUAL_CORE) - -MG_IRAM static bool is_dualbank(void) { - if (IS_DUALCORE()) { - // H745/H755 and H747/H757 are running on dual core. - // Using only the 1st bank (mapped to CM7), in order not to interfere - // with the 2nd bank (CM4), possibly causing CM4 to boot unexpectedly. - return false; - } - return (s_mg_flash_stm32h7.size < 2 * 1024 * 1024) ? false : true; -} - -MG_IRAM static void flash_unlock(void) { - static bool unlocked = false; - if (unlocked == false) { - MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab; - if (is_dualbank()) { - MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123; - MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab; - } - MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared" - MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once - unlocked = true; - } -} - -MG_IRAM static bool flash_page_start(volatile uint32_t *dst) { - char *base = (char *) s_mg_flash_stm32h7.start, - *end = base + s_mg_flash_stm32h7.size; - volatile char *p = (char *) dst; - return p >= base && p < end && ((p - base) % s_mg_flash_stm32h7.secsz) == 0; -} - -MG_IRAM static bool flash_is_err(uint32_t bank) { - return MG_REG(bank + FLASH_SR) & ((MG_BIT(11) - 1) << 17); // RM0433 4.9.5 -} - -MG_IRAM static void flash_wait(uint32_t bank) { - while (MG_REG(bank + FLASH_SR) & (MG_BIT(0) | MG_BIT(2))) (void) 0; -} - -MG_IRAM static void flash_clear_err(uint32_t bank) { - flash_wait(bank); // Wait until ready - MG_REG(bank + FLASH_CCR) = ((MG_BIT(11) - 1) << 16U); // Clear all errors -} - -MG_IRAM static bool flash_bank_is_swapped(uint32_t bank) { - return MG_REG(bank + FLASH_OPTCR) & MG_BIT(31); // RM0433 4.9.7 -} - -// Figure out flash bank based on the address -MG_IRAM static uint32_t flash_bank(void *addr) { - size_t ofs = (char *) addr - (char *) s_mg_flash_stm32h7.start; - if (!is_dualbank()) return FLASH_BASE1; - return ofs < s_mg_flash_stm32h7.size / 2 ? FLASH_BASE1 : FLASH_BASE2; -} - -// read-while-write, no need to disable IRQs for standalone usage -MG_IRAM static bool mg_stm32h7_erase(void *addr) { - bool ok = false; - if (flash_page_start(addr) == false) { - MG_ERROR(("%p is not on a sector boundary", addr)); - } else { - uintptr_t diff = (char *) addr - (char *) s_mg_flash_stm32h7.start; - uint32_t sector = diff / s_mg_flash_stm32h7.secsz; - uint32_t bank = flash_bank(addr); - uint32_t saved_cr = MG_REG(bank + FLASH_CR); // Save CR value - - flash_unlock(); - if (sector > 7) sector -= 8; - - flash_clear_err(bank); - MG_REG(bank + FLASH_CR) = MG_BIT(5); // 32-bit write parallelism - MG_REG(bank + FLASH_CR) |= (sector & 7U) << 8U; // Sector to erase - MG_REG(bank + FLASH_CR) |= MG_BIT(2); // Sector erase bit - MG_REG(bank + FLASH_CR) |= MG_BIT(7); // Start erasing - ok = !flash_is_err(bank); - MG_DEBUG(("Erase sector %lu @ %p %s. CR %#lx SR %#lx", sector, addr, - ok ? "ok" : "fail", MG_REG(bank + FLASH_CR), - MG_REG(bank + FLASH_SR))); - MG_REG(bank + FLASH_CR) = saved_cr; // Restore CR - } - return ok; -} - -MG_IRAM static bool mg_stm32h7_swap(void) { - if (!is_dualbank()) return true; - uint32_t bank = FLASH_BASE1; - uint32_t desired = flash_bank_is_swapped(bank) ? 0 : MG_BIT(31); - flash_unlock(); - flash_clear_err(bank); - // printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG); - MG_SET_BITS(MG_REG(bank + FLASH_OPTSR_PRG), MG_BIT(31), desired); - // printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG); - MG_REG(bank + FLASH_OPTCR) |= MG_BIT(1); // OPTSTART - while ((MG_REG(bank + FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0; - return true; -} - -static bool s_flash_irq_disabled; - -MG_IRAM static bool mg_stm32h7_write(void *addr, const void *buf, size_t len) { - if ((len % s_mg_flash_stm32h7.align) != 0) { - MG_ERROR(("%lu is not aligned to %lu", len, s_mg_flash_stm32h7.align)); - return false; - } - uint32_t bank = flash_bank(addr); - uint32_t *dst = (uint32_t *) addr; - uint32_t *src = (uint32_t *) buf; - uint32_t *end = (uint32_t *) ((char *) buf + len); - bool ok = true; - MG_ARM_DISABLE_IRQ(); - flash_unlock(); - flash_clear_err(bank); - MG_REG(bank + FLASH_CR) = MG_BIT(1); // Set programming flag - MG_REG(bank + FLASH_CR) |= MG_BIT(5); // 32-bit write parallelism - while (ok && src < end) { - if (flash_page_start(dst) && mg_stm32h7_erase(dst) == false) { - ok = false; - break; - } - *(volatile uint32_t *) dst++ = *src++; - flash_wait(bank); - if (flash_is_err(bank)) ok = false; - } - if (!s_flash_irq_disabled) MG_ARM_ENABLE_IRQ(); - MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst, - ok ? "ok" : "fail", MG_REG(bank + FLASH_CR), - MG_REG(bank + FLASH_SR))); - MG_REG(bank + FLASH_CR) &= ~MG_BIT(1); // Clear programming flag - return ok; -} - -// just overwrite instead of swap -MG_IRAM static void single_bank_swap(char *p1, char *p2, size_t s, size_t ss) { - // no stdlib calls here - for (size_t ofs = 0; ofs < s; ofs += ss) { - mg_stm32h7_write(p1 + ofs, p2 + ofs, ss); - } - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; -} - -bool mg_ota_begin(size_t new_firmware_size) { - s_mg_flash_stm32h7.size = MG_REG(FLASH_SIZE_REG) * 1024; - if (IS_DUALCORE()) { - // Using only the 1st bank (mapped to CM7) - s_mg_flash_stm32h7.size /= 2; - } - return mg_ota_flash_begin(new_firmware_size, &s_mg_flash_stm32h7); -} - -bool mg_ota_write(const void *buf, size_t len) { - return mg_ota_flash_write(buf, len, &s_mg_flash_stm32h7); -} - -bool mg_ota_end(void) { - if (mg_ota_flash_end(&s_mg_flash_stm32h7)) { - if (is_dualbank()) { - // Bank swap is deferred until reset, been executing in flash, reset - *(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; - } else { - // Swap partitions. Pray power does not go away - MG_INFO(("Swapping partitions, size %u (%u sectors)", - s_mg_flash_stm32h7.size, - s_mg_flash_stm32h7.size / s_mg_flash_stm32h7.secsz)); - MG_INFO(("Do NOT power off...")); - mg_log_level = MG_LL_NONE; - s_flash_irq_disabled = true; - // Runs in RAM, will reset when finished - single_bank_swap( - (char *) s_mg_flash_stm32h7.start, - (char *) s_mg_flash_stm32h7.start + s_mg_flash_stm32h7.size / 2, - s_mg_flash_stm32h7.size / 2, s_mg_flash_stm32h7.secsz); - } - } - return false; -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/printf.c" -#endif - - - - -size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) { - size_t len = mg_snprintf(NULL, 0, fmt, ap); - char *buf; - if (len == 0 || mg_queue_book(q, &buf, len + 1) < len + 1) { - len = 0; // Nah. Not enough space - } else { - len = mg_vsnprintf((char *) buf, len + 1, fmt, ap); - mg_queue_add(q, len); - } - return len; -} - -size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) { - va_list ap; - size_t len; - va_start(ap, fmt); - len = mg_queue_vprintf(q, fmt, &ap); - va_end(ap); - return len; -} - -static void mg_pfn_iobuf_private(char ch, void *param, bool expand) { - struct mg_iobuf *io = (struct mg_iobuf *) param; - if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2); - if (io->len + 2 <= io->size) { - io->buf[io->len++] = (uint8_t) ch; - io->buf[io->len] = 0; - } else if (io->len < io->size) { - io->buf[io->len++] = 0; // Guarantee to 0-terminate - } -} - -static void mg_putchar_iobuf_static(char ch, void *param) { - mg_pfn_iobuf_private(ch, param, false); -} - -void mg_pfn_iobuf(char ch, void *param) { - mg_pfn_iobuf_private(ch, param, true); -} - -size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap) { - struct mg_iobuf io = {(uint8_t *) buf, len, 0, 0}; - size_t n = mg_vxprintf(mg_putchar_iobuf_static, &io, fmt, ap); - if (n < len) buf[n] = '\0'; - return n; -} - -size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...) { - va_list ap; - size_t n; - va_start(ap, fmt); - n = mg_vsnprintf(buf, len, fmt, &ap); - va_end(ap); - return n; -} - -char *mg_vmprintf(const char *fmt, va_list *ap) { - struct mg_iobuf io = {0, 0, 0, 256}; - mg_vxprintf(mg_pfn_iobuf, &io, fmt, ap); - return (char *) io.buf; -} - -char *mg_mprintf(const char *fmt, ...) { - char *s; - va_list ap; - va_start(ap, fmt); - s = mg_vmprintf(fmt, &ap); - va_end(ap); - return s; -} - -void mg_pfn_stdout(char c, void *param) { - putchar(c); - (void) param; -} - -static size_t print_ip4(void (*out)(char, void *), void *arg, uint8_t *p) { - return mg_xprintf(out, arg, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); -} - -static size_t print_ip6(void (*out)(char, void *), void *arg, uint16_t *p) { - return mg_xprintf(out, arg, "[%x:%x:%x:%x:%x:%x:%x:%x]", mg_ntohs(p[0]), - mg_ntohs(p[1]), mg_ntohs(p[2]), mg_ntohs(p[3]), - mg_ntohs(p[4]), mg_ntohs(p[5]), mg_ntohs(p[6]), - mg_ntohs(p[7])); -} - -size_t mg_print_ip4(void (*out)(char, void *), void *arg, va_list *ap) { - uint8_t *p = va_arg(*ap, uint8_t *); - return print_ip4(out, arg, p); -} - -size_t mg_print_ip6(void (*out)(char, void *), void *arg, va_list *ap) { - uint16_t *p = va_arg(*ap, uint16_t *); - return print_ip6(out, arg, p); -} - -size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap) { - struct mg_addr *addr = va_arg(*ap, struct mg_addr *); - if (addr->is_ip6) return print_ip6(out, arg, (uint16_t *) addr->ip); - return print_ip4(out, arg, (uint8_t *) &addr->ip); -} - -size_t mg_print_ip_port(void (*out)(char, void *), void *arg, va_list *ap) { - struct mg_addr *a = va_arg(*ap, struct mg_addr *); - return mg_xprintf(out, arg, "%M:%hu", mg_print_ip, a, mg_ntohs(a->port)); -} - -size_t mg_print_mac(void (*out)(char, void *), void *arg, va_list *ap) { - uint8_t *p = va_arg(*ap, uint8_t *); - return mg_xprintf(out, arg, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], - p[3], p[4], p[5]); -} - -static char mg_esc(int c, bool esc) { - const char *p, *esc1 = "\b\f\n\r\t\\\"", *esc2 = "bfnrt\\\""; - for (p = esc ? esc1 : esc2; *p != '\0'; p++) { - if (*p == c) return esc ? esc2[p - esc1] : esc1[p - esc2]; - } - return 0; -} - -static char mg_escape(int c) { - return mg_esc(c, true); -} - -static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf, - size_t len) { - size_t i = 0, extra = 0; - for (i = 0; i < len && buf[i] != '\0'; i++) { - char c = mg_escape(buf[i]); - if (c) { - out('\\', ptr), out(c, ptr), extra++; - } else { - out(buf[i], ptr); - } - } - return i + extra; -} - -static size_t bcpy(void (*out)(char, void *), void *arg, uint8_t *buf, - size_t len) { - size_t i, j, n = 0; - const char *t = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - for (i = 0; i < len; i += 3) { - uint8_t c1 = buf[i], c2 = i + 1 < len ? buf[i + 1] : 0, - c3 = i + 2 < len ? buf[i + 2] : 0; - char tmp[4] = {t[c1 >> 2], t[(c1 & 3) << 4 | (c2 >> 4)], '=', '='}; - if (i + 1 < len) tmp[2] = t[(c2 & 15) << 2 | (c3 >> 6)]; - if (i + 2 < len) tmp[3] = t[c3 & 63]; - for (j = 0; j < sizeof(tmp) && tmp[j] != '\0'; j++) out(tmp[j], arg); - n += j; - } - return n; -} - -size_t mg_print_hex(void (*out)(char, void *), void *arg, va_list *ap) { - size_t bl = (size_t) va_arg(*ap, int); - uint8_t *p = va_arg(*ap, uint8_t *); - const char *hex = "0123456789abcdef"; - size_t j; - for (j = 0; j < bl; j++) { - out(hex[(p[j] >> 4) & 0x0F], arg); - out(hex[p[j] & 0x0F], arg); - } - return 2 * bl; -} -size_t mg_print_base64(void (*out)(char, void *), void *arg, va_list *ap) { - size_t len = (size_t) va_arg(*ap, int); - uint8_t *buf = va_arg(*ap, uint8_t *); - return bcpy(out, arg, buf, len); -} - -size_t mg_print_esc(void (*out)(char, void *), void *arg, va_list *ap) { - size_t len = (size_t) va_arg(*ap, int); - char *p = va_arg(*ap, char *); - if (len == 0) len = p == NULL ? 0 : strlen(p); - return qcpy(out, arg, p, len); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/queue.c" -#endif - - - -#if (defined(__GNUC__) && (__GNUC__ > 4) || \ - (defined(__GNUC_MINOR__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 1)) || \ - defined(__clang__) -#define MG_MEMORY_BARRIER() __sync_synchronize() -#elif defined(_MSC_VER) && _MSC_VER >= 1700 -#define MG_MEMORY_BARRIER() MemoryBarrier() -#elif !defined(MG_MEMORY_BARRIER) -#define MG_MEMORY_BARRIER() -#endif - -// Every message in a queue is prepended by a 32-bit message length (ML). -// If ML is 0, then it is the end, and reader must wrap to the beginning. -// -// Queue when q->tail <= q->head: -// |----- free -----| ML | message1 | ML | message2 | ----- free ------| -// ^ ^ ^ ^ -// buf tail head len -// -// Queue when q->tail > q->head: -// | ML | message2 |----- free ------| ML | message1 | 0 |---- free ----| -// ^ ^ ^ ^ -// buf head tail len - -void mg_queue_init(struct mg_queue *q, char *buf, size_t size) { - q->size = size; - q->buf = buf; - q->head = q->tail = 0; -} - -static size_t mg_queue_read_len(struct mg_queue *q) { - uint32_t n = 0; - MG_MEMORY_BARRIER(); - memcpy(&n, q->buf + q->tail, sizeof(n)); - assert(q->tail + n + sizeof(n) <= q->size); - return n; -} - -static void mg_queue_write_len(struct mg_queue *q, size_t len) { - uint32_t n = (uint32_t) len; - memcpy(q->buf + q->head, &n, sizeof(n)); - MG_MEMORY_BARRIER(); -} - -size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len) { - size_t space = 0, hs = sizeof(uint32_t) * 2; // *2 is for the 0 marker - if (q->head >= q->tail && q->head + len + hs <= q->size) { - space = q->size - q->head - hs; // There is enough space - } else if (q->head >= q->tail && q->tail > hs) { - mg_queue_write_len(q, 0); // Not enough space ahead - q->head = 0; // Wrap head to the beginning - } - if (q->head + hs + len < q->tail) space = q->tail - q->head - hs; - if (buf != NULL) *buf = q->buf + q->head + sizeof(uint32_t); - return space; -} - -size_t mg_queue_next(struct mg_queue *q, char **buf) { - size_t len = 0; - if (q->tail != q->head) { - len = mg_queue_read_len(q); - if (len == 0) { // Zero (head wrapped) ? - q->tail = 0; // Reset tail to the start - if (q->head > q->tail) len = mg_queue_read_len(q); // Read again - } - } - if (buf != NULL) *buf = q->buf + q->tail + sizeof(uint32_t); - assert(q->tail + len <= q->size); - return len; -} - -void mg_queue_add(struct mg_queue *q, size_t len) { - assert(len > 0); - mg_queue_write_len(q, len); - assert(q->head + sizeof(uint32_t) * 2 + len <= q->size); - q->head += len + sizeof(uint32_t); -} - -void mg_queue_del(struct mg_queue *q, size_t len) { - q->tail += len + sizeof(uint32_t); - assert(q->tail + sizeof(uint32_t) <= q->size); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/rpc.c" -#endif - - - -void mg_rpc_add(struct mg_rpc **head, struct mg_str method, - void (*fn)(struct mg_rpc_req *), void *fn_data) { - struct mg_rpc *rpc = (struct mg_rpc *) calloc(1, sizeof(*rpc)); - if (rpc != NULL) { - rpc->method = mg_strdup(method); - rpc->fn = fn; - rpc->fn_data = fn_data; - rpc->next = *head, *head = rpc; - } -} - -void mg_rpc_del(struct mg_rpc **head, void (*fn)(struct mg_rpc_req *)) { - struct mg_rpc *r; - while ((r = *head) != NULL) { - if (r->fn == fn || fn == NULL) { - *head = r->next; - free((void *) r->method.buf); - free(r); - } else { - head = &(*head)->next; - } - } -} - -static void mg_rpc_call(struct mg_rpc_req *r, struct mg_str method) { - struct mg_rpc *h = r->head == NULL ? NULL : *r->head; - while (h != NULL && !mg_match(method, h->method, NULL)) h = h->next; - if (h != NULL) { - r->rpc = h; - h->fn(r); - } else { - mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) method.len, method.buf); - } -} - -void mg_rpc_process(struct mg_rpc_req *r) { - int len, off = mg_json_get(r->frame, "$.method", &len); - if (off > 0 && r->frame.buf[off] == '"') { - struct mg_str method = mg_str_n(&r->frame.buf[off + 1], (size_t) len - 2); - mg_rpc_call(r, method); - } else if ((off = mg_json_get(r->frame, "$.result", &len)) > 0 || - (off = mg_json_get(r->frame, "$.error", &len)) > 0) { - mg_rpc_call(r, mg_str("")); // JSON response! call "" method handler - } else { - mg_rpc_err(r, -32700, "%m", mg_print_esc, (int) r->frame.len, - r->frame.buf); // Invalid - } -} - -void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) { - int len, off = mg_json_get(r->frame, "$.id", &len); - if (off > 0) { - mg_xprintf(r->pfn, r->pfn_data, "{%m:%.*s,%m:", mg_print_esc, 0, "id", len, - &r->frame.buf[off], mg_print_esc, 0, "result"); - mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); - mg_xprintf(r->pfn, r->pfn_data, "}"); - } -} - -void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_rpc_vok(r, fmt, &ap); - va_end(ap); -} - -void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) { - int len, off = mg_json_get(r->frame, "$.id", &len); - mg_xprintf(r->pfn, r->pfn_data, "{"); - if (off > 0) { - mg_xprintf(r->pfn, r->pfn_data, "%m:%.*s,", mg_print_esc, 0, "id", len, - &r->frame.buf[off]); - } - mg_xprintf(r->pfn, r->pfn_data, "%m:{%m:%d,%m:", mg_print_esc, 0, "error", - mg_print_esc, 0, "code", code, mg_print_esc, 0, "message"); - mg_vxprintf(r->pfn, r->pfn_data, fmt == NULL ? "null" : fmt, ap); - mg_xprintf(r->pfn, r->pfn_data, "}}"); -} - -void mg_rpc_err(struct mg_rpc_req *r, int code, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - mg_rpc_verr(r, code, fmt, &ap); - va_end(ap); -} - -static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) { - struct mg_rpc *h, **head = (struct mg_rpc **) va_arg(*ap, void **); - size_t len = 0; - for (h = *head; h != NULL; h = h->next) { - if (h->method.len == 0) continue; // Ignore response handler - len += mg_xprintf(pfn, pfn_data, "%s%m", h == *head ? "" : ",", - mg_print_esc, (int) h->method.len, h->method.buf); - } - return len; -} - -void mg_rpc_list(struct mg_rpc_req *r) { - mg_rpc_ok(r, "[%M]", print_methods, r->head); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sha1.c" -#endif -/* Copyright(c) By Steve Reid */ -/* 100% Public Domain */ - - - -union char64long16 { - unsigned char c[64]; - uint32_t l[16]; -}; - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -static uint32_t blk0(union char64long16 *block, int i) { - if (MG_BIG_ENDIAN) { - } else { - block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | - (rol(block->l[i], 8) & 0x00FF00FF); - } - return block->l[i]; -} - -/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ -#undef blk -#undef R0 -#undef R1 -#undef R2 -#undef R3 -#undef R4 - -#define blk(i) \ - (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ \ - block->l[(i + 2) & 15] ^ block->l[i & 15], \ - 1)) -#define R0(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v, w, x, y, z, i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ - w = rol(w, 30); -#define R3(v, w, x, y, z, i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v, w, x, y, z, i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w = rol(w, 30); - -static void mg_sha1_transform(uint32_t state[5], - const unsigned char *buffer) { - uint32_t a, b, c, d, e; - union char64long16 block[1]; - - memcpy(block, buffer, 64); - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - R0(a, b, c, d, e, 0); - R0(e, a, b, c, d, 1); - R0(d, e, a, b, c, 2); - R0(c, d, e, a, b, 3); - R0(b, c, d, e, a, 4); - R0(a, b, c, d, e, 5); - R0(e, a, b, c, d, 6); - R0(d, e, a, b, c, 7); - R0(c, d, e, a, b, 8); - R0(b, c, d, e, a, 9); - R0(a, b, c, d, e, 10); - R0(e, a, b, c, d, 11); - R0(d, e, a, b, c, 12); - R0(c, d, e, a, b, 13); - R0(b, c, d, e, a, 14); - R0(a, b, c, d, e, 15); - R1(e, a, b, c, d, 16); - R1(d, e, a, b, c, 17); - R1(c, d, e, a, b, 18); - R1(b, c, d, e, a, 19); - R2(a, b, c, d, e, 20); - R2(e, a, b, c, d, 21); - R2(d, e, a, b, c, 22); - R2(c, d, e, a, b, 23); - R2(b, c, d, e, a, 24); - R2(a, b, c, d, e, 25); - R2(e, a, b, c, d, 26); - R2(d, e, a, b, c, 27); - R2(c, d, e, a, b, 28); - R2(b, c, d, e, a, 29); - R2(a, b, c, d, e, 30); - R2(e, a, b, c, d, 31); - R2(d, e, a, b, c, 32); - R2(c, d, e, a, b, 33); - R2(b, c, d, e, a, 34); - R2(a, b, c, d, e, 35); - R2(e, a, b, c, d, 36); - R2(d, e, a, b, c, 37); - R2(c, d, e, a, b, 38); - R2(b, c, d, e, a, 39); - R3(a, b, c, d, e, 40); - R3(e, a, b, c, d, 41); - R3(d, e, a, b, c, 42); - R3(c, d, e, a, b, 43); - R3(b, c, d, e, a, 44); - R3(a, b, c, d, e, 45); - R3(e, a, b, c, d, 46); - R3(d, e, a, b, c, 47); - R3(c, d, e, a, b, 48); - R3(b, c, d, e, a, 49); - R3(a, b, c, d, e, 50); - R3(e, a, b, c, d, 51); - R3(d, e, a, b, c, 52); - R3(c, d, e, a, b, 53); - R3(b, c, d, e, a, 54); - R3(a, b, c, d, e, 55); - R3(e, a, b, c, d, 56); - R3(d, e, a, b, c, 57); - R3(c, d, e, a, b, 58); - R3(b, c, d, e, a, 59); - R4(a, b, c, d, e, 60); - R4(e, a, b, c, d, 61); - R4(d, e, a, b, c, 62); - R4(c, d, e, a, b, 63); - R4(b, c, d, e, a, 64); - R4(a, b, c, d, e, 65); - R4(e, a, b, c, d, 66); - R4(d, e, a, b, c, 67); - R4(c, d, e, a, b, 68); - R4(b, c, d, e, a, 69); - R4(a, b, c, d, e, 70); - R4(e, a, b, c, d, 71); - R4(d, e, a, b, c, 72); - R4(c, d, e, a, b, 73); - R4(b, c, d, e, a, 74); - R4(a, b, c, d, e, 75); - R4(e, a, b, c, d, 76); - R4(d, e, a, b, c, 77); - R4(c, d, e, a, b, 78); - R4(b, c, d, e, a, 79); - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Erase working structures. The order of operations is important, - * used to ensure that compiler doesn't optimize those out. */ - memset(block, 0, sizeof(block)); - a = b = c = d = e = 0; - (void) a; - (void) b; - (void) c; - (void) d; - (void) e; -} - -void mg_sha1_init(mg_sha1_ctx *context) { - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -void mg_sha1_update(mg_sha1_ctx *context, const unsigned char *data, - size_t len) { - size_t i, j; - - j = context->count[0]; - if ((context->count[0] += (uint32_t) len << 3) < j) context->count[1]++; - context->count[1] += (uint32_t) (len >> 29); - j = (j >> 3) & 63; - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64 - j)); - mg_sha1_transform(context->state, context->buffer); - for (; i + 63 < len; i += 64) { - mg_sha1_transform(context->state, &data[i]); - } - j = 0; - } else - i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - -void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *context) { - unsigned i; - unsigned char finalcount[8], c; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> - ((3 - (i & 3)) * 8)) & - 255); - } - c = 0200; - mg_sha1_update(context, &c, 1); - while ((context->count[0] & 504) != 448) { - c = 0000; - mg_sha1_update(context, &c, 1); - } - mg_sha1_update(context, finalcount, 8); - for (i = 0; i < 20; i++) { - digest[i] = - (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); - } - memset(context, '\0', sizeof(*context)); - memset(&finalcount, '\0', sizeof(finalcount)); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sha256.c" -#endif -// https://github.com/B-Con/crypto-algorithms -// Author: Brad Conte (brad AT bradconte.com) -// Disclaimer: This code is presented "as is" without any guarantees. -// Details: Defines the API for the corresponding SHA1 implementation. -// Copyright: public domain - - - -#define ror(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) -#define ch(x, y, z) (((x) & (y)) ^ (~(x) & (z))) -#define maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define ep0(x) (ror(x, 2) ^ ror(x, 13) ^ ror(x, 22)) -#define ep1(x) (ror(x, 6) ^ ror(x, 11) ^ ror(x, 25)) -#define sig0(x) (ror(x, 7) ^ ror(x, 18) ^ ((x) >> 3)) -#define sig1(x) (ror(x, 17) ^ ror(x, 19) ^ ((x) >> 10)) - -static const uint32_t mg_sha256_k[64] = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; - -void mg_sha256_init(mg_sha256_ctx *ctx) { - ctx->len = 0; - ctx->bits = 0; - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; -} - -static void mg_sha256_chunk(mg_sha256_ctx *ctx) { - int i, j; - uint32_t a, b, c, d, e, f, g, h; - uint32_t m[64]; - for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (uint32_t) (((uint32_t) ctx->buffer[j] << 24) | - ((uint32_t) ctx->buffer[j + 1] << 16) | - ((uint32_t) ctx->buffer[j + 2] << 8) | - ((uint32_t) ctx->buffer[j + 3])); - for (; i < 64; ++i) - m[i] = sig1(m[i - 2]) + m[i - 7] + sig0(m[i - 15]) + m[i - 16]; - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 64; ++i) { - uint32_t t1 = h + ep1(e) + ch(e, f, g) + mg_sha256_k[i] + m[i]; - uint32_t t2 = ep0(a) + maj(a, b, c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -void mg_sha256_update(mg_sha256_ctx *ctx, const unsigned char *data, - size_t len) { - size_t i; - for (i = 0; i < len; i++) { - ctx->buffer[ctx->len] = data[i]; - if ((++ctx->len) == 64) { - mg_sha256_chunk(ctx); - ctx->bits += 512; - ctx->len = 0; - } - } -} - -// TODO: make final reusable (remove side effects) -void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *ctx) { - uint32_t i = ctx->len; - if (i < 56) { - ctx->buffer[i++] = 0x80; - while (i < 56) { - ctx->buffer[i++] = 0x00; - } - } else { - ctx->buffer[i++] = 0x80; - while (i < 64) { - ctx->buffer[i++] = 0x00; - } - mg_sha256_chunk(ctx); - memset(ctx->buffer, 0, 56); - } - - ctx->bits += ctx->len * 8; - ctx->buffer[63] = (uint8_t) ((ctx->bits) & 0xff); - ctx->buffer[62] = (uint8_t) ((ctx->bits >> 8) & 0xff); - ctx->buffer[61] = (uint8_t) ((ctx->bits >> 16) & 0xff); - ctx->buffer[60] = (uint8_t) ((ctx->bits >> 24) & 0xff); - ctx->buffer[59] = (uint8_t) ((ctx->bits >> 32) & 0xff); - ctx->buffer[58] = (uint8_t) ((ctx->bits >> 40) & 0xff); - ctx->buffer[57] = (uint8_t) ((ctx->bits >> 48) & 0xff); - ctx->buffer[56] = (uint8_t) ((ctx->bits >> 56) & 0xff); - mg_sha256_chunk(ctx); - - for (i = 0; i < 4; ++i) { - digest[i] = (uint8_t) ((ctx->state[0] >> (24 - i * 8)) & 0xff); - digest[i + 4] = (uint8_t) ((ctx->state[1] >> (24 - i * 8)) & 0xff); - digest[i + 8] = (uint8_t) ((ctx->state[2] >> (24 - i * 8)) & 0xff); - digest[i + 12] = (uint8_t) ((ctx->state[3] >> (24 - i * 8)) & 0xff); - digest[i + 16] = (uint8_t) ((ctx->state[4] >> (24 - i * 8)) & 0xff); - digest[i + 20] = (uint8_t) ((ctx->state[5] >> (24 - i * 8)) & 0xff); - digest[i + 24] = (uint8_t) ((ctx->state[6] >> (24 - i * 8)) & 0xff); - digest[i + 28] = (uint8_t) ((ctx->state[7] >> (24 - i * 8)) & 0xff); - } -} - -void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data, - size_t datasz) { - mg_sha256_ctx ctx; - uint8_t k[64] = {0}; - uint8_t o_pad[64], i_pad[64]; - unsigned int i; - memset(i_pad, 0x36, sizeof(i_pad)); - memset(o_pad, 0x5c, sizeof(o_pad)); - if (keysz < 64) { - if (keysz > 0) memmove(k, key, keysz); - } else { - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, key, keysz); - mg_sha256_final(k, &ctx); - } - for (i = 0; i < sizeof(k); i++) { - i_pad[i] ^= k[i]; - o_pad[i] ^= k[i]; - } - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, i_pad, sizeof(i_pad)); - mg_sha256_update(&ctx, data, datasz); - mg_sha256_final(dst, &ctx); - mg_sha256_init(&ctx); - mg_sha256_update(&ctx, o_pad, sizeof(o_pad)); - mg_sha256_update(&ctx, dst, 32); - mg_sha256_final(dst, &ctx); -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sntp.c" -#endif - - - - - - -#define SNTP_TIME_OFFSET 2208988800U // (1970 - 1900) in seconds -#define SNTP_MAX_FRAC 4294967295.0 // 2 ** 32 - 1 - -static uint64_t s_boot_timestamp = 0; // Updated by SNTP - -uint64_t mg_now(void) { - return mg_millis() + s_boot_timestamp; -} - -static int64_t gettimestamp(const uint32_t *data) { - uint32_t sec = mg_ntohl(data[0]), frac = mg_ntohl(data[1]); - if (sec) sec -= SNTP_TIME_OFFSET; - return ((int64_t) sec) * 1000 + (int64_t) (frac / SNTP_MAX_FRAC * 1000.0); -} - -int64_t mg_sntp_parse(const unsigned char *buf, size_t len) { - int64_t epoch_milliseconds = -1; - int mode = len > 0 ? buf[0] & 7 : 0; - int version = len > 0 ? (buf[0] >> 3) & 7 : 0; - if (len < 48) { - MG_ERROR(("%s", "corrupt packet")); - } else if (mode != 4 && mode != 5) { - MG_ERROR(("%s", "not a server reply")); - } else if (buf[1] == 0) { - MG_ERROR(("%s", "server sent a kiss of death")); - } else if (version == 4 || version == 3) { - // int64_t ref = gettimestamp((uint32_t *) &buf[16]); - int64_t origin_time = gettimestamp((uint32_t *) &buf[24]); - int64_t receive_time = gettimestamp((uint32_t *) &buf[32]); - int64_t transmit_time = gettimestamp((uint32_t *) &buf[40]); - int64_t now = (int64_t) mg_millis(); - int64_t latency = (now - origin_time) - (transmit_time - receive_time); - epoch_milliseconds = transmit_time + latency / 2; - s_boot_timestamp = (uint64_t) (epoch_milliseconds - now); - } else { - MG_ERROR(("unexpected version: %d", version)); - } - return epoch_milliseconds; -} - -static void sntp_cb(struct mg_connection *c, int ev, void *ev_data) { - uint64_t *expiration_time = (uint64_t *) c->data; - if (ev == MG_EV_OPEN) { - *expiration_time = mg_millis() + 3000; // Store expiration time in 3s - } else if (ev == MG_EV_CONNECT) { - mg_sntp_request(c); - } else if (ev == MG_EV_READ) { - int64_t milliseconds = mg_sntp_parse(c->recv.buf, c->recv.len); - if (milliseconds > 0) { - s_boot_timestamp = (uint64_t) milliseconds - mg_millis(); - mg_call(c, MG_EV_SNTP_TIME, (uint64_t *) &milliseconds); - MG_DEBUG(("%lu got time: %lld ms from epoch", c->id, milliseconds)); - } - // mg_iobuf_del(&c->recv, 0, c->recv.len); // Free receive buffer - c->is_closing = 1; - } else if (ev == MG_EV_POLL) { - if (mg_millis() > *expiration_time) c->is_closing = 1; - } else if (ev == MG_EV_CLOSE) { - } - (void) ev_data; -} - -void mg_sntp_request(struct mg_connection *c) { - if (c->is_resolving) { - MG_ERROR(("%lu wait until resolved", c->id)); - } else { - int64_t now = (int64_t) mg_millis(); // Use int64_t, for vc98 - uint8_t buf[48] = {0}; - uint32_t *t = (uint32_t *) &buf[40]; - double frac = ((double) (now % 1000)) / 1000.0 * SNTP_MAX_FRAC; - buf[0] = (0 << 6) | (4 << 3) | 3; - t[0] = mg_htonl((uint32_t) (now / 1000) + SNTP_TIME_OFFSET); - t[1] = mg_htonl((uint32_t) frac); - mg_send(c, buf, sizeof(buf)); - } -} - -struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fnd) { - struct mg_connection *c = NULL; - if (url == NULL) url = "udp://time.google.com:123"; - if ((c = mg_connect(mgr, url, fn, fnd)) != NULL) { - c->pfn = sntp_cb; - sntp_cb(c, MG_EV_OPEN, (void *) url); - } - return c; -} - -#ifdef MG_ENABLE_LINES -#line 1 "src/sock.c" -#endif - - - - - - - - - - - -#if MG_ENABLE_SOCKET - -#ifndef closesocket -#define closesocket(x) close(x) -#endif - -#define FD(c_) ((MG_SOCKET_TYPE) (size_t) (c_)->fd) -#define S2PTR(s_) ((void *) (size_t) (s_)) - -#ifndef MSG_NONBLOCKING -#define MSG_NONBLOCKING 0 -#endif - -#ifndef AF_INET6 -#define AF_INET6 10 -#endif - -#ifndef MG_SOCK_ERR -#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? errno : 0) -#endif - -#ifndef MG_SOCK_INTR -#define MG_SOCK_INTR(fd) (fd == MG_INVALID_SOCKET && MG_SOCK_ERR(-1) == EINTR) -#endif - -#ifndef MG_SOCK_PENDING -#define MG_SOCK_PENDING(errcode) \ - (((errcode) < 0) && (errno == EINPROGRESS || errno == EWOULDBLOCK)) -#endif - -#ifndef MG_SOCK_RESET -#define MG_SOCK_RESET(errcode) \ - (((errcode) < 0) && (errno == EPIPE || errno == ECONNRESET)) -#endif - -union usa { - struct sockaddr sa; - struct sockaddr_in sin; -#if MG_ENABLE_IPV6 - struct sockaddr_in6 sin6; -#endif -}; - -static socklen_t tousa(struct mg_addr *a, union usa *usa) { - socklen_t len = sizeof(usa->sin); - memset(usa, 0, sizeof(*usa)); - usa->sin.sin_family = AF_INET; - usa->sin.sin_port = a->port; - memcpy(&usa->sin.sin_addr, a->ip, sizeof(uint32_t)); -#if MG_ENABLE_IPV6 - if (a->is_ip6) { - usa->sin.sin_family = AF_INET6; - usa->sin6.sin6_port = a->port; - usa->sin6.sin6_scope_id = a->scope_id; - memcpy(&usa->sin6.sin6_addr, a->ip, sizeof(a->ip)); - len = sizeof(usa->sin6); - } -#endif - return len; -} - -static void tomgaddr(union usa *usa, struct mg_addr *a, bool is_ip6) { - a->is_ip6 = is_ip6; - a->port = usa->sin.sin_port; - memcpy(&a->ip, &usa->sin.sin_addr, sizeof(uint32_t)); -#if MG_ENABLE_IPV6 - if (is_ip6) { - memcpy(a->ip, &usa->sin6.sin6_addr, sizeof(a->ip)); - a->port = usa->sin6.sin6_port; - a->scope_id = (uint8_t) usa->sin6.sin6_scope_id; - } -#endif -} - -static void setlocaddr(MG_SOCKET_TYPE fd, struct mg_addr *addr) { - union usa usa; - socklen_t n = sizeof(usa); - if (getsockname(fd, &usa.sa, &n) == 0) { - tomgaddr(&usa, addr, n != sizeof(usa.sin)); - } -} - -static void iolog(struct mg_connection *c, char *buf, long n, bool r) { - if (n == MG_IO_WAIT) { - // Do nothing - } else if (n <= 0) { - c->is_closing = 1; // Termination. Don't call mg_error(): #1529 - } else if (n > 0) { - if (c->is_hexdumping) { - MG_INFO(("\n-- %lu %M %s %M %ld", c->id, mg_print_ip_port, &c->loc, - r ? "<-" : "->", mg_print_ip_port, &c->rem, n)); - mg_hexdump(buf, (size_t) n); - } - if (r) { - c->recv.len += (size_t) n; - mg_call(c, MG_EV_READ, &n); - } else { - mg_iobuf_del(&c->send, 0, (size_t) n); - // if (c->send.len == 0) mg_iobuf_resize(&c->send, 0); - if (c->send.len == 0) { - MG_EPOLL_MOD(c, 0); - } - mg_call(c, MG_EV_WRITE, &n); - } - } -} - -long mg_io_send(struct mg_connection *c, const void *buf, size_t len) { - long n; - if (c->is_udp) { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - n = sendto(FD(c), (char *) buf, len, 0, &usa.sa, slen); - if (n > 0) setlocaddr(FD(c), &c->loc); - } else { - n = send(FD(c), (char *) buf, len, MSG_NONBLOCKING); - } - MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n))); - if (MG_SOCK_PENDING(n)) return MG_IO_WAIT; - if (MG_SOCK_RESET(n)) return MG_IO_RESET; - if (n <= 0) return MG_IO_ERR; - return n; -} - -bool mg_send(struct mg_connection *c, const void *buf, size_t len) { - if (c->is_udp) { - long n = mg_io_send(c, buf, len); - MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len, - c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n))); - iolog(c, (char *) buf, n, false); - return n > 0; - } else { - return mg_iobuf_add(&c->send, c->send.len, buf, len); - } -} - -static void mg_set_non_blocking_mode(MG_SOCKET_TYPE fd) { -#if defined(MG_CUSTOM_NONBLOCK) - MG_CUSTOM_NONBLOCK(fd); -#elif MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - unsigned long on = 1; - ioctlsocket(fd, FIONBIO, &on); -#elif MG_ENABLE_RL - unsigned long on = 1; - ioctlsocket(fd, FIONBIO, &on); -#elif MG_ENABLE_FREERTOS_TCP - const BaseType_t off = 0; - if (setsockopt(fd, 0, FREERTOS_SO_RCVTIMEO, &off, sizeof(off)) != 0) (void) 0; - if (setsockopt(fd, 0, FREERTOS_SO_SNDTIMEO, &off, sizeof(off)) != 0) (void) 0; -#elif MG_ENABLE_LWIP - lwip_fcntl(fd, F_SETFL, O_NONBLOCK); -#elif MG_ARCH == MG_ARCH_AZURERTOS - fcntl(fd, F_SETFL, O_NONBLOCK); -#elif MG_ARCH == MG_ARCH_TIRTOS - int val = 0; - setsockopt(fd, SOL_SOCKET, SO_BLOCKING, &val, sizeof(val)); - // SPRU524J section 3.3.3 page 63, SO_SNDLOWAT - int sz = sizeof(val); - getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &val, &sz); - val /= 2; // set send low-water mark at half send buffer size - setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)); -#else - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); // Non-blocking mode - fcntl(fd, F_SETFD, FD_CLOEXEC); // Set close-on-exec -#endif -} - -bool mg_open_listener(struct mg_connection *c, const char *url) { - MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; - bool success = false; - c->loc.port = mg_htons(mg_url_port(url)); - if (!mg_aton(mg_url_host(url), &c->loc)) { - MG_ERROR(("invalid listening URL: %s", url)); - } else { - union usa usa; - socklen_t slen = tousa(&c->loc, &usa); - int rc, on = 1, af = c->loc.is_ip6 ? AF_INET6 : AF_INET; - int type = strncmp(url, "udp:", 4) == 0 ? SOCK_DGRAM : SOCK_STREAM; - int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; - (void) on; - - if ((fd = socket(af, type, proto)) == MG_INVALID_SOCKET) { - MG_ERROR(("socket: %d", MG_SOCK_ERR(-1))); -#if defined(SO_EXCLUSIVEADDRUSE) - } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, - (char *) &on, sizeof(on))) != 0) { - // "Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE" - MG_ERROR(("setsockopt(SO_EXCLUSIVEADDRUSE): %d %d", on, MG_SOCK_ERR(rc))); -#elif defined(SO_REUSEADDR) && (!defined(LWIP_SOCKET) || SO_REUSE) - } else if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, - sizeof(on))) != 0) { - // 1. SO_REUSEADDR semantics on UNIX and Windows is different. On - // Windows, SO_REUSEADDR allows to bind a socket to a port without error - // even if the port is already open by another program. This is not the - // behavior SO_REUSEADDR was designed for, and leads to hard-to-track - // failure scenarios. - // - // 2. For LWIP, SO_REUSEADDR should be explicitly enabled by defining - // SO_REUSE = 1 in lwipopts.h, otherwise the code below will compile but - // won't work! (setsockopt will return EINVAL) - MG_ERROR(("setsockopt(SO_REUSEADDR): %d", MG_SOCK_ERR(rc))); -#endif -#if MG_IPV6_V6ONLY - // Bind only to the V6 address, not V4 address on this port - } else if (c->loc.is_ip6 && - (rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, - sizeof(on))) != 0) { - // See #2089. Allow to bind v4 and v6 sockets on the same port - MG_ERROR(("setsockopt(IPV6_V6ONLY): %d", MG_SOCK_ERR(rc))); -#endif - } else if ((rc = bind(fd, &usa.sa, slen)) != 0) { - MG_ERROR(("bind: %d", MG_SOCK_ERR(rc))); - } else if ((type == SOCK_STREAM && - (rc = listen(fd, MG_SOCK_LISTEN_BACKLOG_SIZE)) != 0)) { - // NOTE(lsm): FreeRTOS uses backlog value as a connection limit - // In case port was set to 0, get the real port number - MG_ERROR(("listen: %d", MG_SOCK_ERR(rc))); - } else { - setlocaddr(fd, &c->loc); - mg_set_non_blocking_mode(fd); - c->fd = S2PTR(fd); - MG_EPOLL_ADD(c); - success = true; - } - } - if (success == false && fd != MG_INVALID_SOCKET) closesocket(fd); - return success; -} - -static long recv_raw(struct mg_connection *c, void *buf, size_t len) { - long n = 0; - if (c->is_udp) { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen); - if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin)); - } else { - n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING); - } - MG_VERBOSE(("%lu %ld %d", c->id, n, MG_SOCK_ERR(n))); - if (MG_SOCK_PENDING(n)) return MG_IO_WAIT; - if (MG_SOCK_RESET(n)) return MG_IO_RESET; - if (n <= 0) return MG_IO_ERR; - return n; -} - -static bool ioalloc(struct mg_connection *c, struct mg_iobuf *io) { - bool res = false; - if (io->len >= MG_MAX_RECV_SIZE) { - mg_error(c, "MG_MAX_RECV_SIZE"); - } else if (io->size <= io->len && - !mg_iobuf_resize(io, io->size + MG_IO_SIZE)) { - mg_error(c, "OOM"); - } else { - res = true; - } - return res; -} - -// NOTE(lsm): do only one iteration of reads, cause some systems -// (e.g. FreeRTOS stack) return 0 instead of -1/EWOULDBLOCK when no data -static void read_conn(struct mg_connection *c) { - if (ioalloc(c, &c->recv)) { - char *buf = (char *) &c->recv.buf[c->recv.len]; - size_t len = c->recv.size - c->recv.len; - long n = -1; - if (c->is_tls) { - // Do not read to the raw TLS buffer if it already has enough. - // This is to prevent overflowing c->rtls if our reads are slow - long m; - if (c->rtls.len < 16 * 1024 + 40) { // TLS record, header, MAC, padding - if (!ioalloc(c, &c->rtls)) return; - n = recv_raw(c, (char *) &c->rtls.buf[c->rtls.len], - c->rtls.size - c->rtls.len); - if (n > 0) c->rtls.len += (size_t) n; - } - // there can still be > 16K from last iteration, always mg_tls_recv() - m = c->is_tls_hs ? (long) MG_IO_WAIT : mg_tls_recv(c, buf, len); - if (n == MG_IO_ERR) { - if (c->rtls.len == 0 || m < 0) { - // Close only when we have fully drained both rtls and TLS buffers - c->is_closing = 1; // or there's nothing we can do about it. - m = MG_IO_ERR; - } else { // see #2885 - // TLS buffer is capped to max record size, even though, there can - // be more than one record, give TLS a chance to process them. - } - } else if (c->is_tls_hs) { - mg_tls_handshake(c); - } - n = m; - } else { - n = recv_raw(c, buf, len); - } - MG_DEBUG(("%lu %ld %lu:%lu:%lu %ld err %d", c->id, c->fd, c->send.len, - c->recv.len, c->rtls.len, n, MG_SOCK_ERR(n))); - iolog(c, buf, n, true); - } -} - -static void write_conn(struct mg_connection *c) { - char *buf = (char *) c->send.buf; - size_t len = c->send.len; - long n = c->is_tls ? mg_tls_send(c, buf, len) : mg_io_send(c, buf, len); - MG_DEBUG(("%lu %ld snd %ld/%ld rcv %ld/%ld n=%ld err=%d", c->id, c->fd, - (long) c->send.len, (long) c->send.size, (long) c->recv.len, - (long) c->recv.size, n, MG_SOCK_ERR(n))); - iolog(c, buf, n, false); -} - -static void close_conn(struct mg_connection *c) { - if (FD(c) != MG_INVALID_SOCKET) { -#if MG_ENABLE_EPOLL - epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_DEL, FD(c), NULL); -#endif - closesocket(FD(c)); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_FD_CLR(c->fd, c->mgr->ss, eSELECT_ALL); -#endif - } - mg_close_conn(c); -} - -static void connect_conn(struct mg_connection *c) { - union usa usa; - socklen_t n = sizeof(usa); - // Use getpeername() to test whether we have connected - if (getpeername(FD(c), &usa.sa, &n) == 0) { - c->is_connecting = 0; - setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_CONNECT, NULL); - MG_EPOLL_MOD(c, 0); - if (c->is_tls_hs) mg_tls_handshake(c); - } else { - mg_error(c, "socket error"); - } -} - -static void setsockopts(struct mg_connection *c) { -#if MG_ENABLE_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS || \ - MG_ARCH == MG_ARCH_TIRTOS - (void) c; -#else - int on = 1; -#if !defined(SOL_TCP) -#define SOL_TCP IPPROTO_TCP -#endif - if (setsockopt(FD(c), SOL_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) != 0) - (void) 0; - if (setsockopt(FD(c), SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) != - 0) - (void) 0; -#endif -} - -void mg_connect_resolved(struct mg_connection *c) { - int type = c->is_udp ? SOCK_DGRAM : SOCK_STREAM; - int proto = type == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; - int rc, af = c->rem.is_ip6 ? AF_INET6 : AF_INET; // c->rem has resolved IP - c->fd = S2PTR(socket(af, type, proto)); // Create outbound socket - c->is_resolving = 0; // Clear resolving flag - if (FD(c) == MG_INVALID_SOCKET) { - mg_error(c, "socket(): %d", MG_SOCK_ERR(-1)); - } else if (c->is_udp) { - MG_EPOLL_ADD(c); -#if MG_ARCH == MG_ARCH_TIRTOS - union usa usa; // TI-RTOS NDK requires binding to receive on UDP sockets - socklen_t slen = tousa(&c->loc, &usa); - if ((rc = bind(c->fd, &usa.sa, slen)) != 0) - MG_ERROR(("bind: %d", MG_SOCK_ERR(rc))); -#endif - setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_RESOLVE, NULL); - mg_call(c, MG_EV_CONNECT, NULL); - } else { - union usa usa; - socklen_t slen = tousa(&c->rem, &usa); - mg_set_non_blocking_mode(FD(c)); - setsockopts(c); - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_RESOLVE, NULL); - rc = connect(FD(c), &usa.sa, slen); // Attempt to connect - if (rc == 0) { // Success - setlocaddr(FD(c), &c->loc); - mg_call(c, MG_EV_CONNECT, NULL); // Send MG_EV_CONNECT to the user - } else if (MG_SOCK_PENDING(rc)) { // Need to wait for TCP handshake - MG_DEBUG(("%lu %ld -> %M pend", c->id, c->fd, mg_print_ip_port, &c->rem)); - c->is_connecting = 1; - } else { - mg_error(c, "connect: %d", MG_SOCK_ERR(rc)); - } - } -} - -static MG_SOCKET_TYPE raccept(MG_SOCKET_TYPE sock, union usa *usa, - socklen_t *len) { - MG_SOCKET_TYPE fd = MG_INVALID_SOCKET; - do { - memset(usa, 0, sizeof(*usa)); - fd = accept(sock, &usa->sa, len); - } while (MG_SOCK_INTR(fd)); - return fd; -} - -static void accept_conn(struct mg_mgr *mgr, struct mg_connection *lsn) { - struct mg_connection *c = NULL; - union usa usa; - socklen_t sa_len = sizeof(usa); - MG_SOCKET_TYPE fd = raccept(FD(lsn), &usa, &sa_len); - if (fd == MG_INVALID_SOCKET) { -#if MG_ARCH == MG_ARCH_AZURERTOS || defined(__ECOS) - // AzureRTOS, in non-block socket mode can mark listening socket readable - // even it is not. See comment for 'select' func implementation in - // nx_bsd.c That's not an error, just should try later - if (errno != EAGAIN) -#endif - MG_ERROR(("%lu accept failed, errno %d", lsn->id, MG_SOCK_ERR(-1))); -#if (MG_ARCH != MG_ARCH_WIN32) && !MG_ENABLE_FREERTOS_TCP && \ - (MG_ARCH != MG_ARCH_TIRTOS) && !MG_ENABLE_POLL && !MG_ENABLE_EPOLL - } else if ((long) fd >= FD_SETSIZE) { - MG_ERROR(("%ld > %ld", (long) fd, (long) FD_SETSIZE)); - closesocket(fd); -#endif - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("%lu OOM", lsn->id)); - closesocket(fd); - } else { - tomgaddr(&usa, &c->rem, sa_len != sizeof(usa.sin)); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fd = S2PTR(fd); - MG_EPOLL_ADD(c); - mg_set_non_blocking_mode(FD(c)); - setsockopts(c); - c->is_accepted = 1; - c->is_hexdumping = lsn->is_hexdumping; - c->loc = lsn->loc; - c->pfn = lsn->pfn; - c->pfn_data = lsn->pfn_data; - c->fn = lsn->fn; - c->fn_data = lsn->fn_data; - MG_DEBUG(("%lu %ld accepted %M -> %M", c->id, c->fd, mg_print_ip_port, - &c->rem, mg_print_ip_port, &c->loc)); - mg_call(c, MG_EV_OPEN, NULL); - mg_call(c, MG_EV_ACCEPT, NULL); - } -} - -static bool can_read(const struct mg_connection *c) { - return c->is_full == false; -} - -static bool can_write(const struct mg_connection *c) { - return c->is_connecting || (c->send.len > 0 && c->is_tls_hs == 0); -} - -static bool skip_iotest(const struct mg_connection *c) { - return (c->is_closing || c->is_resolving || FD(c) == MG_INVALID_SOCKET) || - (can_read(c) == false && can_write(c) == false); -} - -static void mg_iotest(struct mg_mgr *mgr, int ms) { -#if MG_ENABLE_FREERTOS_TCP - struct mg_connection *c; - for (c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) continue; - if (can_read(c)) - FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_READ | eSELECT_EXCEPT); - if (can_write(c)) FreeRTOS_FD_SET(c->fd, mgr->ss, eSELECT_WRITE); - if (c->is_closing) ms = 1; - } - FreeRTOS_select(mgr->ss, pdMS_TO_TICKS(ms)); - for (c = mgr->conns; c != NULL; c = c->next) { - EventBits_t bits = FreeRTOS_FD_ISSET(c->fd, mgr->ss); - c->is_readable = bits & (eSELECT_READ | eSELECT_EXCEPT) ? 1U : 0; - c->is_writable = bits & eSELECT_WRITE ? 1U : 0; - if (c->fd != MG_INVALID_SOCKET) - FreeRTOS_FD_CLR(c->fd, mgr->ss, - eSELECT_READ | eSELECT_EXCEPT | eSELECT_WRITE); - } -#elif MG_ENABLE_EPOLL - size_t max = 1; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1, c->is_readable = 1; - if (can_write(c)) MG_EPOLL_MOD(c, 1); - if (c->is_closing) ms = 1; - max++; - } - struct epoll_event *evs = (struct epoll_event *) alloca(max * sizeof(evs[0])); - int n = epoll_wait(mgr->epoll_fd, evs, (int) max, ms); - for (int i = 0; i < n; i++) { - struct mg_connection *c = (struct mg_connection *) evs[i].data.ptr; - if (evs[i].events & EPOLLERR) { - mg_error(c, "socket error"); - } else if (c->is_readable == 0) { - bool rd = evs[i].events & (EPOLLIN | EPOLLHUP); - bool wr = evs[i].events & EPOLLOUT; - c->is_readable = can_read(c) && rd ? 1U : 0; - c->is_writable = can_write(c) && wr ? 1U : 0; - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1; - } - } - (void) skip_iotest; -#elif MG_ENABLE_POLL - nfds_t n = 0; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) n++; - struct pollfd *fds = (struct pollfd *) alloca(n * sizeof(fds[0])); - memset(fds, 0, n * sizeof(fds[0])); - n = 0; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (c->is_closing) ms = 1; - if (skip_iotest(c)) { - // Socket not valid, ignore - } else { - // Don't wait if TLS is ready - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) ms = 1; - fds[n].fd = FD(c); - if (can_read(c)) fds[n].events |= POLLIN; - if (can_write(c)) fds[n].events |= POLLOUT; - n++; - } - } - - // MG_INFO(("poll n=%d ms=%d", (int) n, ms)); - if (poll(fds, n, ms) < 0) { -#if MG_ARCH == MG_ARCH_WIN32 - if (n == 0) Sleep(ms); // On Windows, poll fails if no sockets -#endif - memset(fds, 0, n * sizeof(fds[0])); - } - n = 0; - for (struct mg_connection *c = mgr->conns; c != NULL; c = c->next) { - if (skip_iotest(c)) { - // Socket not valid, ignore - } else { - if (fds[n].revents & POLLERR) { - mg_error(c, "socket error"); - } else { - c->is_readable = - (unsigned) (fds[n].revents & (POLLIN | POLLHUP) ? 1 : 0); - c->is_writable = (unsigned) (fds[n].revents & POLLOUT ? 1 : 0); - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1; - } - n++; - } - } -#else - struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}, *tvp; - struct mg_connection *c; - fd_set rset, wset, eset; - MG_SOCKET_TYPE maxfd = 0; - int rc; - - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - tvp = ms < 0 ? NULL : &tv; - for (c = mgr->conns; c != NULL; c = c->next) { - c->is_readable = c->is_writable = 0; - if (skip_iotest(c)) continue; - FD_SET(FD(c), &eset); - if (can_read(c)) FD_SET(FD(c), &rset); - if (can_write(c)) FD_SET(FD(c), &wset); - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) tvp = &tv_zero; - if (FD(c) > maxfd) maxfd = FD(c); - if (c->is_closing) tvp = &tv_zero; - } - - if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) { -#if MG_ARCH == MG_ARCH_WIN32 - if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets -#else - MG_ERROR(("select: %d %d", rc, MG_SOCK_ERR(rc))); -#endif - FD_ZERO(&rset); - FD_ZERO(&wset); - FD_ZERO(&eset); - } - - for (c = mgr->conns; c != NULL; c = c->next) { - if (FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &eset)) { - mg_error(c, "socket error"); - } else { - c->is_readable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &rset); - c->is_writable = FD(c) != MG_INVALID_SOCKET && FD_ISSET(FD(c), &wset); - if (c->rtls.len > 0 || mg_tls_pending(c) > 0) c->is_readable = 1; - } - } -#endif -} - -static bool mg_socketpair(MG_SOCKET_TYPE sp[2], union usa usa[2]) { - socklen_t n = sizeof(usa[0].sin); - bool success = false; - - sp[0] = sp[1] = MG_INVALID_SOCKET; - (void) memset(&usa[0], 0, sizeof(usa[0])); - usa[0].sin.sin_family = AF_INET; - *(uint32_t *) &usa->sin.sin_addr = mg_htonl(0x7f000001U); // 127.0.0.1 - usa[1] = usa[0]; - - if ((sp[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && - (sp[1] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != MG_INVALID_SOCKET && - bind(sp[0], &usa[0].sa, n) == 0 && // - bind(sp[1], &usa[1].sa, n) == 0 && // - getsockname(sp[0], &usa[0].sa, &n) == 0 && // - getsockname(sp[1], &usa[1].sa, &n) == 0 && // - connect(sp[0], &usa[1].sa, n) == 0 && // - connect(sp[1], &usa[0].sa, n) == 0) { // - success = true; - } - if (!success) { - if (sp[0] != MG_INVALID_SOCKET) closesocket(sp[0]); - if (sp[1] != MG_INVALID_SOCKET) closesocket(sp[1]); - sp[0] = sp[1] = MG_INVALID_SOCKET; - } - return success; -} - -// mg_wakeup() event handler -static void wufn(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_READ) { - unsigned long *id = (unsigned long *) c->recv.buf; - // MG_INFO(("Got data")); - // mg_hexdump(c->recv.buf, c->recv.len); - if (c->recv.len >= sizeof(*id)) { - struct mg_connection *t; - for (t = c->mgr->conns; t != NULL; t = t->next) { - if (t->id == *id) { - struct mg_str data = mg_str_n((char *) c->recv.buf + sizeof(*id), - c->recv.len - sizeof(*id)); - mg_call(t, MG_EV_WAKEUP, &data); - } - } - } - c->recv.len = 0; // Consume received data - } else if (ev == MG_EV_CLOSE) { - closesocket(c->mgr->pipe); // When we're closing, close the other - c->mgr->pipe = MG_INVALID_SOCKET; // side of the socketpair, too - } - (void) ev_data; -} - -bool mg_wakeup_init(struct mg_mgr *mgr) { - bool ok = false; - if (mgr->pipe == MG_INVALID_SOCKET) { - union usa usa[2]; - MG_SOCKET_TYPE sp[2] = {MG_INVALID_SOCKET, MG_INVALID_SOCKET}; - struct mg_connection *c = NULL; - if (!mg_socketpair(sp, usa)) { - MG_ERROR(("Cannot create socket pair")); - } else if ((c = mg_wrapfd(mgr, (int) sp[1], wufn, NULL)) == NULL) { - closesocket(sp[0]); - closesocket(sp[1]); - sp[0] = sp[1] = MG_INVALID_SOCKET; - } else { - tomgaddr(&usa[0], &c->rem, false); - MG_DEBUG(("%lu %p pipe %lu", c->id, c->fd, (unsigned long) sp[0])); - mgr->pipe = sp[0]; - ok = true; - } - } - return ok; -} - -bool mg_wakeup(struct mg_mgr *mgr, unsigned long conn_id, const void *buf, - size_t len) { - if (mgr->pipe != MG_INVALID_SOCKET && conn_id > 0) { - char *extended_buf = (char *) alloca(len + sizeof(conn_id)); - memcpy(extended_buf, &conn_id, sizeof(conn_id)); - memcpy(extended_buf + sizeof(conn_id), buf, len); - send(mgr->pipe, extended_buf, len + sizeof(conn_id), MSG_NONBLOCKING); - return true; - } - return false; -} - -void mg_mgr_poll(struct mg_mgr *mgr, int ms) { - struct mg_connection *c, *tmp; - uint64_t now; - - mg_iotest(mgr, ms); - now = mg_millis(); - mg_timer_poll(&mgr->timers, now); - - for (c = mgr->conns; c != NULL; c = tmp) { - bool is_resp = c->is_resp; - tmp = c->next; - mg_call(c, MG_EV_POLL, &now); - if (is_resp && !c->is_resp) { - long n = 0; - mg_call(c, MG_EV_READ, &n); - } - MG_VERBOSE(("%lu %c%c %c%c%c%c%c %lu %lu", c->id, - c->is_readable ? 'r' : '-', c->is_writable ? 'w' : '-', - c->is_tls ? 'T' : 't', c->is_connecting ? 'C' : 'c', - c->is_tls_hs ? 'H' : 'h', c->is_resolving ? 'R' : 'r', - c->is_closing ? 'C' : 'c', mg_tls_pending(c), c->rtls.len)); - if (c->is_resolving || c->is_closing) { - // Do nothing - } else if (c->is_listening && c->is_udp == 0) { - if (c->is_readable) accept_conn(mgr, c); - } else if (c->is_connecting) { - if (c->is_readable || c->is_writable) connect_conn(c); - //} else if (c->is_tls_hs) { - // if ((c->is_readable || c->is_writable)) mg_tls_handshake(c); - } else { - if (c->is_readable) read_conn(c); - if (c->is_writable) write_conn(c); - } - - if (c->is_draining && c->send.len == 0) c->is_closing = 1; - if (c->is_closing) close_conn(c); - } -} -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/ssi.c" -#endif - - - - -#ifndef MG_MAX_SSI_DEPTH -#define MG_MAX_SSI_DEPTH 5 -#endif - -#ifndef MG_SSI_BUFSIZ -#define MG_SSI_BUFSIZ 1024 -#endif - -#if MG_ENABLE_SSI -static char *mg_ssi(const char *path, const char *root, int depth) { - struct mg_iobuf b = {NULL, 0, 0, MG_IO_SIZE}; - FILE *fp = fopen(path, "rb"); - if (fp != NULL) { - char buf[MG_SSI_BUFSIZ], arg[sizeof(buf)]; - int ch, intag = 0; - size_t len = 0; - buf[0] = arg[0] = '\0'; - while ((ch = fgetc(fp)) != EOF) { - if (intag && ch == '>' && buf[len - 1] == '-' && buf[len - 2] == '-') { - buf[len++] = (char) (ch & 0xff); - buf[len] = '\0'; - if (sscanf(buf, " %#x %#x", s_txdesc[s_txno][1], tsr)); - if (!(s_txdesc[s_txno][1] & MG_BIT(31))) s_txdesc[s_txno][1] |= MG_BIT(31); - } - - GMAC_REGS->GMAC_RSR = rsr; - GMAC_REGS->GMAC_TSR = tsr; -} - -struct mg_tcpip_driver mg_tcpip_driver_same54 = { - mg_tcpip_driver_same54_init, mg_tcpip_driver_same54_tx, NULL, - mg_tcpip_driver_same54_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/stm32f.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \ - MG_ENABLE_DRIVER_STM32F -struct stm32f_eth { - volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR, - MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR, - MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR, - MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, - RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR, - RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR, - RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR, - PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564], - DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER, - DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR, - DMACHRBAR; -}; -#undef ETH -#define ETH ((struct stm32f_eth *) (uintptr_t) 0x40028000) - -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static uint8_t s_txno; // Current TX descriptor -static uint8_t s_rxno; // Current RX descriptor - -static struct mg_tcpip_if *s_ifp; // MIP interface - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - ETH->MACMIIAR &= (7 << 2); - ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6); - ETH->MACMIIAR |= MG_BIT(0); - while (ETH->MACMIIAR & MG_BIT(0)) (void) 0; - return ETH->MACMIIDR & 0xffff; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH->MACMIIDR = val; - ETH->MACMIIAR &= (7 << 2); - ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1); - ETH->MACMIIAR |= MG_BIT(0); - while (ETH->MACMIIAR & MG_BIT(0)) (void) 0; -} - -static uint32_t get_hclk(void) { - struct rcc { - volatile uint32_t CR, PLLCFGR, CFGR; - } *rcc = (struct rcc *) 0x40023800; - uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */; - - if (rcc->CFGR & (1 << 2)) { - clk = hse; - } else if (rcc->CFGR & (1 << 3)) { - uint32_t vco, m, n, p; - m = (rcc->PLLCFGR & (0x3f << 0)) >> 0; - n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6; - p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2; - clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi; - vco = (uint32_t) ((uint64_t) clk * n / m); - clk = vco / p; - } else { - clk = hsi; - } - uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4; - if (hpre < 8) return clk; - - uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div) - return ((uint32_t) clk) >> ahbptab[hpre - 8]; -} - -// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3, -// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived -// from the HSI (internal RC), and it can go above specs, the datasheets -// specify a range of frequencies and activate one of a series of dividers to -// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on -// HCLK with a +5% drift. If the user uses a different clock from our -// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx -// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift) -static int guess_mdc_cr(void) { - uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values - uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers - uint32_t hclk = get_hclk(); // Guess system HCLK - int result = -1; // Invalid CR value - if (hclk < 25000000) { - MG_ERROR(("HCLK too low")); - } else { - for (int i = 0; i < 6; i++) { - if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) MG_ERROR(("HCLK too high")); - } - MG_DEBUG(("HCLK: %u, CR: %d", hclk, result)); - return result; -} - -static bool mg_tcpip_driver_stm32f_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_stm32f_data *d = - (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = MG_BIT(31); // Own - s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained - s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = - (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = - (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - ETH->DMABMR |= MG_BIT(0); // Software reset - while ((ETH->DMABMR & MG_BIT(0)) != 0) (void) 0; // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - ETH->MACMIIAR = ((uint32_t) cr & 7) << 2; - - // NOTE(cpq): we do not use extended descriptor bit 7, and do not use - // hardware checksum. Therefore, descriptor size is 4, not 8 - // ETH->DMABMR = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) | - // MG_BIT(25); - ETH->MACIMR = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT - ETH->MACFCR = MG_BIT(7); // Disable zero quarta pause - // ETH->MACFFR = MG_BIT(31); // Receive all - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, phy_addr, MG_PHY_CLOCKS_MAC); - ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors - ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors - ETH->DMAIER = MG_BIT(6) | MG_BIT(16); // RIE, NISE - ETH->MACCR = - MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast - ETH->DMAOMR = - MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF - - // MAC address filtering - ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; - ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) | - ((uint32_t) ifp->mac[2] << 16) | - ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static size_t mg_tcpip_driver_stm32f_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][1] = (uint32_t) len; // Set data len - s_txdesc[s_txno][0] = MG_BIT(20) | MG_BIT(28) | MG_BIT(29); // Chain,FS,LS - s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - MG_DSB(); // ensure descriptors have been written - ETH->DMASR = MG_BIT(2) | MG_BIT(5); // Clear any prior TBUS/TUS - ETH->DMATPDR = 0; // and resume - return len; -} - -static bool mg_tcpip_driver_stm32f_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_stm32f_data *d = - (struct mg_tcpip_driver_stm32f_data *) ifp->driver_data; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex - if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M - if (full_duplex == false) maccr &= ~MG_BIT(11); // Half-duplex - ETH->MACCR = maccr; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10, - maccr & MG_BIT(11) ? "full" : "half")); - } - return up; -} - -#ifdef __riscv -__attribute__((interrupt())) // For RISCV CH32V307, which share the same MAC -#endif -void ETH_IRQHandler(void); -void ETH_IRQHandler(void) { - if (ETH->DMASR & MG_BIT(6)) { // Frame received, loop - ETH->DMASR = MG_BIT(16) | MG_BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done - if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) == - (MG_BIT(8) | MG_BIT(9))) && - !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames - uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_BIT(14) - 1)); - // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], - // ETH->DMASR); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][0] = MG_BIT(31); - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - // Cleanup flags - ETH->DMASR = MG_BIT(16) // NIS, normal interrupt summary - | MG_BIT(7); // Clear possible RBUS while processing - ETH->DMARPDR = 0; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_stm32f = { - mg_tcpip_driver_stm32f_init, mg_tcpip_driver_stm32f_tx, NULL, - mg_tcpip_driver_stm32f_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/stm32h.c" -#endif - - -#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN) -// STM32H: vendor modded single-queue Synopsys v4.2 -// MCXNx4x: dual-queue Synopsys v5.2 -// RT1170 ENET_QOS: quad-queue Synopsys v5.1 -struct synopsys_enet_qos { - volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R, - RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR, - RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR, - MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR, - MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10, - MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR, - RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR, - MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR, - MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR, - RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR, - RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR, - MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R, - RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R, - RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R, - MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR, - MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR, - RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28, - MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR, - RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR, - MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR, - RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR, - MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR, - RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR, - DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR, - DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER, - DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR, - RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2], - DMACMFCR; -}; -#undef ETH -#if MG_ENABLE_DRIVER_STM32H -#define ETH \ - ((struct synopsys_enet_qos *) (uintptr_t) (0x40000000UL + 0x00020000UL + \ - 0x8000UL)) -#elif MG_ENABLE_DRIVER_MCXN -#define ETH ((struct synopsys_enet_qos *) (uintptr_t) 0x40100000UL) -#endif - -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static struct mg_tcpip_if *s_ifp; // MIP interface - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - ETH->MACMDIOAR &= (0xF << 8); - ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2; - ETH->MACMDIOAR |= MG_BIT(0); - while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0; - return (uint16_t) ETH->MACMDIODR; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH->MACMDIODR = val; - ETH->MACMDIOAR &= (0xF << 8); - ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2; - ETH->MACMDIOAR |= MG_BIT(0); - while (ETH->MACMDIOAR & MG_BIT(0)) (void) 0; -} - -static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_stm32h_data *d = - (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data; - s_ifp = ifp; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - uint8_t phy_conf = d == NULL ? MG_PHY_CLOCKS_MAC : d->phy_conf; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer - } - - ETH->DMAMR |= MG_BIT(0); // Software reset - for (int i = 0; i < 4; i++) - (void) 0; // wait at least 4 clocks before reading - while ((ETH->DMAMR & MG_BIT(0)) != 0) (void) 0; // Wait until done - - // Set MDC clock divider. Get user value, else, assume max freq - int cr = (d == NULL || d->mdc_cr < 0) ? 7 : d->mdc_cr; - ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8; - - // NOTE(scaprile): We do not use timing facilities so the DMA engine does not - // re-write buffer address - ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value) - ETH->DMASBMR |= MG_BIT(12); // AAL NOTE(scaprile): is this actually needed - ETH->MACIER = 0; // Do not enable additional irq sources (reset value) - ETH->MACTFCR = MG_BIT(7); // Disable zero-quanta pause - // ETH->MACPFR = MG_BIT(31); // Receive all - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, phy_addr, phy_conf); - ETH->DMACRDLAR = - (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address - ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length - ETH->DMACRDTPR = - (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - - 1]; // last valid descriptor address - ETH->DMACTDLAR = - (uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address - ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length - ETH->DMACTDTPR = - (uint32_t) (uintptr_t) s_txdesc; // first available descriptor address - ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value) -#if !MG_ENABLE_DRIVER_STM32H - MG_SET_BITS(ETH->DMACTCR, 0x3F << 16, MG_BIT(16)); - MG_SET_BITS(ETH->DMACRCR, 0x3F << 16, MG_BIT(16)); -#endif - ETH->DMACIER = MG_BIT(6) | MG_BIT(15); // RIE, NIE - ETH->MACCR = MG_BIT(0) | MG_BIT(1) | MG_BIT(13) | MG_BIT(14) | - MG_BIT(15); // RE, TE, Duplex, Fast, Reserved -#if MG_ENABLE_DRIVER_STM32H - ETH->MTLTQOMR |= MG_BIT(1); // TSF - ETH->MTLRQOMR |= MG_BIT(5); // RSF -#else - ETH->MTLTQOMR |= (7 << 16) | MG_BIT(3) | MG_BIT(1); // 2KB Q0, TSF - ETH->MTLRQOMR |= (7 << 20) | MG_BIT(5); // 2KB Q, RSF - MG_SET_BITS(ETH->RESERVED6[3], 3, 2); // Enable RxQ0 (MAC_RXQ_CTRL0) -#endif - ETH->DMACTCR |= MG_BIT(0); // ST - ETH->DMACRCR |= MG_BIT(0); // SR - - // MAC address filtering - ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; - ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) | - ((uint32_t) ifp->mac[2] << 16) | - ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; - return true; -} - -static uint32_t s_txno; -static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][3] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno, - s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR)); - for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3])); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][2] = (uint32_t) len; // Set data len - s_txdesc[s_txno][3] = MG_BIT(28) | MG_BIT(29); // FD, LD - s_txdesc[s_txno][3] |= MG_BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - ETH->DMACSR |= MG_BIT(2) | MG_BIT(1); // Clear any prior TBU, TPS - ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume - return len; - (void) ifp; -} - -static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_stm32h_data *d = - (struct mg_tcpip_driver_stm32h_data *) ifp->driver_data; - uint8_t phy_addr = d == NULL ? 0 : d->phy_addr; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(13); // 100M, Full-duplex - if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M - if (full_duplex == false) maccr &= ~MG_BIT(13); // Half-duplex - ETH->MACCR = maccr; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", maccr & MG_BIT(14) ? 100 : 10, - maccr & MG_BIT(13) ? "full" : "half")); - } - return up; -} - -static uint32_t s_rxno; -#if MG_ENABLE_DRIVER_MCXN -void ETHERNET_IRQHandler(void); -void ETHERNET_IRQHandler(void) { -#else -void ETH_IRQHandler(void); -void ETH_IRQHandler(void) { -#endif - if (ETH->DMACSR & MG_BIT(6)) { // Frame received, loop - ETH->DMACSR = MG_BIT(15) | MG_BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][3] & MG_BIT(31)) break; // exit when done - if (((s_rxdesc[s_rxno][3] & (MG_BIT(28) | MG_BIT(29))) == - (MG_BIT(28) | MG_BIT(29))) && - !(s_rxdesc[s_rxno][3] & MG_BIT(15))) { // skip partial/errored frames - uint32_t len = s_rxdesc[s_rxno][3] & (MG_BIT(15) - 1); - // MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3], - // ETH->DMACSR)); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][3] = - MG_BIT(31) | MG_BIT(30) | MG_BIT(24); // OWN, IOC, BUF1V - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - ETH->DMACSR = - MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing - ETH->DMACRDTPR = - (uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_stm32h = { - mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, NULL, - mg_tcpip_driver_stm32h_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/tm4c.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C -struct tm4c_emac { - volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL, - EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS, - EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H, - EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H, - EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL, - EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4, - EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4], - EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR, - EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239], - EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC, - EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD, - EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL, - RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294], - EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR, - EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT, - RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA, - RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS, - EMACEPHYIM, EMACEPHYIMSC; -}; -#undef EMAC -#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000) - -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers -static struct mg_tcpip_if *s_ifp; // MIP interface -enum { - EPHY_ADDR = 0, - EPHYBMCR = 0, - EPHYBMSR = 1, - EPHYSTS = 16 -}; // PHY constants - -static inline void tm4cspin(volatile uint32_t count) { - while (count--) (void) 0; -} - -static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) { - EMAC->EMACMIIADDR &= (0xf << 2); - EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6); - EMAC->EMACMIIADDR |= MG_BIT(0); - while (EMAC->EMACMIIADDR & MG_BIT(0)) tm4cspin(1); - return EMAC->EMACMIIDATA; -} - -static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { - EMAC->EMACMIIDATA = val; - EMAC->EMACMIIADDR &= (0xf << 2); - EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | MG_BIT(1); - EMAC->EMACMIIADDR |= MG_BIT(0); - while (EMAC->EMACMIIADDR & MG_BIT(0)) tm4cspin(1); -} - -static uint32_t get_sysclk(void) { - struct sysctl { - volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0, - PLLFREQ1; - } *sysctl = (struct sysctl *) 0x400FE000; - uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */; - if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL - uint32_t fin, vco, mdiv, n, q, psysdiv; - uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24; - if (pllsrc == 0) { - clk = piosc; - } else if (pllsrc == 3) { - clk = mosc; - } else { - MG_ERROR(("Unsupported clock source")); - } - q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8; - n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0; - fin = clk / ((q + 1) * (n + 1)); - mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >> - 0; // mint + (mfrac / 1024); MFRAC not supported - psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0; - vco = (uint32_t) ((uint64_t) fin * mdiv); - return vco / (psysdiv + 1); - } - uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20; - if (oscsrc == 0) { - clk = piosc; - } else if (oscsrc == 3) { - clk = mosc; - } else { - MG_ERROR(("Unsupported clock source")); - } - uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16; - return clk / (osysdiv + 1); -} - -// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per -// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be -// derived from the PIOSC (internal RC), and it can go above specs, the -// datasheets specify a range of frequencies and activate one of a series of -// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider -// setting based on SYSCLK with a +5% drift. If the user uses a different clock -// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7) -// (4.5% worst case drift) -// The PHY receives the main oscillator (MOSC) (20.3.1) -static int guess_mdc_cr(void) { - uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values - uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers - uint32_t sysclk = get_sysclk(); // Guess system SYSCLK - int result = -1; // Invalid CR value - if (sysclk < 25000000) { - MG_ERROR(("SYSCLK too low")); - } else { - for (int i = 0; i < 4; i++) { - if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) { - result = crs[i]; - break; - } - } - if (result < 0) MG_ERROR(("SYSCLK too high")); - } - MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result)); - return result; -} - -static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_tm4c_data *d = - (struct mg_tcpip_driver_tm4c_data *) ifp->driver_data; - s_ifp = ifp; - - // Init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = MG_BIT(31); // Own - s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | MG_BIT(14); // 2nd address chained - s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = - (uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain - // MG_DEBUG(("%d %p", i, s_rxdesc[i])); - } - - // Init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = - (uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain - } - - EMAC->EMACDMABUSMOD |= MG_BIT(0); // Software reset - while ((EMAC->EMACDMABUSMOD & MG_BIT(0)) != 0) tm4cspin(1); // Wait until done - - // Set MDC clock divider. If user told us the value, use it. Otherwise, guess - int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr; - EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2; - - // NOTE(cpq): we do not use extended descriptor bit 7, and do not use - // hardware checksum. Therefore, descriptor size is 4, not 8 - // EMAC->EMACDMABUSMOD = MG_BIT(13) | MG_BIT(16) | MG_BIT(22) | MG_BIT(23) | MG_BIT(25); - EMAC->EMACIM = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT - EMAC->EMACFLOWCTL = MG_BIT(7); // Disable zero-quanta pause - // EMAC->EMACFRAMEFLTR = MG_BIT(31); // Receive all - // EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode - emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(15)); // Reset internal PHY (EPHY) - emac_write_phy(EPHY_ADDR, EPHYBMCR, MG_BIT(12)); // Set autonegotiation - EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors - EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors - EMAC->EMACDMAIM = MG_BIT(6) | MG_BIT(16); // RIE, NIE - EMAC->EMACCFG = MG_BIT(2) | MG_BIT(3) | MG_BIT(11) | MG_BIT(14); // RE, TE, Duplex, Fast - EMAC->EMACDMAOPMODE = - MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF - EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4]; - EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) | - ((uint32_t) ifp->mac[2] << 16) | - ((uint32_t) ifp->mac[1] << 8) | ifp->mac[0]; - // NOTE(scaprile) There are 3 additional slots for filtering, disabled by - // default. This also applies to the STM32 driver (at least for F7) - return true; -} - -static uint32_t s_txno; -static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // fail - } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No descriptors available")); - // printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) - // EMAC->EMACDMARIS); - len = 0; // fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - s_txdesc[s_txno][1] = (uint32_t) len; // Set data len - s_txdesc[s_txno][0] = - MG_BIT(20) | MG_BIT(28) | MG_BIT(29) | MG_BIT(30); // Chain,FS,LS,IC - s_txdesc[s_txno][0] |= MG_BIT(31); // Set OWN bit - let DMA take over - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - EMAC->EMACDMARIS = MG_BIT(2) | MG_BIT(5); // Clear any prior TU/UNF - EMAC->EMACTXPOLLD = 0; // and resume - return len; - (void) ifp; -} - -static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) { - uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR); - bool up = (bmsr & MG_BIT(2)) ? 1 : 0; - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS); - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t emaccfg = EMAC->EMACCFG | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex - if (sts & MG_BIT(1)) emaccfg &= ~MG_BIT(14); // 10M - if ((sts & MG_BIT(2)) == 0) emaccfg &= ~MG_BIT(11); // Half-duplex - EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register - MG_DEBUG(("Link is %uM %s-duplex", emaccfg & MG_BIT(14) ? 100 : 10, - emaccfg & MG_BIT(11) ? "full" : "half")); - } - return up; -} - -void EMAC0_IRQHandler(void); -static uint32_t s_rxno; -void EMAC0_IRQHandler(void) { - if (EMAC->EMACDMARIS & MG_BIT(6)) { // Frame received, loop - EMAC->EMACDMARIS = MG_BIT(16) | MG_BIT(6); // Clear flag - for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever - if (s_rxdesc[s_rxno][0] & MG_BIT(31)) break; // exit when done - if (((s_rxdesc[s_rxno][0] & (MG_BIT(8) | MG_BIT(9))) == (MG_BIT(8) | MG_BIT(9))) && - !(s_rxdesc[s_rxno][0] & MG_BIT(15))) { // skip partial/errored frames - uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (MG_BIT(14) - 1)); - // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], - // EMAC->EMACDMARIS); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - } - s_rxdesc[s_rxno][0] = MG_BIT(31); - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - EMAC->EMACDMARIS = MG_BIT(7); // Clear possible RU while processing - EMAC->EMACRXPOLLD = 0; // and resume RX -} - -struct mg_tcpip_driver mg_tcpip_driver_tm4c = {mg_tcpip_driver_tm4c_init, - mg_tcpip_driver_tm4c_tx, NULL, - mg_tcpip_driver_tm4c_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/tms570.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TMS570) && MG_ENABLE_DRIVER_TMS570 -struct tms570_emac_ctrl { - volatile uint32_t REVID, SOFTRESET, RESERVED1[1], INTCONTROL, C0RXTHRESHEN, - C0RXEN, C0TXEN, C0MISCEN, RESERVED2[8], - C0RXTHRESHSTAT, C0RXSTAT, C0TXSTAT, C0MISCSTAT, - RESERVED3[8], - C0RXIMAX, C0TXIMAX; -}; -struct tms570_emac { - volatile uint32_t TXREVID, TXCONTROL, TXTEARDOWN, RESERVED1[1], RXREVID, - RXCONTROL, RXTEARDOWN, RESERVED2[25], TXINTSTATRAW,TXINTSTATMASKED, - TXINTMASKSET, TXINTMASKCLEAR, MACINVECTOR, MACEOIVECTOR, RESERVED8[2], RXINTSTATRAW, - RXINTSTATMASKED, RXINTMASKSET, RXINTMASKCLEAR, MACINTSTATRAW, MACINTSTATMASKED, - MACINTMASKSET, MACINTMASKCLEAR, RESERVED3[16], RXMBPENABLE, RXUNICASTSET, - RXUNICASTCLEAR, RXMAXLEN, RXBUFFEROFFSET, RXFILTERLOWTHRESH, RESERVED9[2], RXFLOWTHRESH[8], - RXFREEBUFFER[8], MACCONTROL, MACSTATUS, EMCONTROL, FIFOCONTROL, MACCONFIG, - SOFTRESET, RESERVED4[22], MACSRCADDRLO, MACSRCADDRHI, MACHASH1, MACHASH2, - BOFFTEST, TPACETEST, RXPAUSE, TXPAUSE, RESERVED5[4], RXGOODFRAMES, RXBCASTFRAMES, - RXMCASTFRAMES, RXPAUSEFRAMES, RXCRCERRORS, RXALIGNCODEERRORS, RXOVERSIZED, - RXJABBER, RXUNDERSIZED, RXFRAGMENTS, RXFILTERED, RXQOSFILTERED, RXOCTETS, - TXGOODFRAMES, TXBCASTFRAMES, TXMCASTFRAMES, TXPAUSEFRAMES, TXDEFERRED, - TXCOLLISION, TXSINGLECOLL, TXMULTICOLL, TXEXCESSIVECOLL, TXLATECOLL, - TXUNDERRUN, TXCARRIERSENSE, TXOCTETS, FRAME64, FRAME65T127, FRAME128T255, - FRAME256T511, FRAME512T1023, FRAME1024TUP, NETOCTETS, RXSOFOVERRUNS, - RXMOFOVERRUNS, RXDMAOVERRUNS, RESERVED6[156], MACADDRLO, MACADDRHI, - MACINDEX, RESERVED7[61], TXHDP[8], RXHDP[8], TXCP[8], RXCP[8]; -}; -struct tms570_mdio { - volatile uint32_t REVID, CONTROL, ALIVE, LINK, LINKINTRAW, LINKINTMASKED, - RESERVED1[2], USERINTRAW, USERINTMASKED, USERINTMASKSET, USERINTMASKCLEAR, - RESERVED2[20], USERACCESS0, USERPHYSEL0, USERACCESS1, USERPHYSEL1; -}; -#define SWAP32(x) ( (((x) & 0x000000FF) << 24) | \ - (((x) & 0x0000FF00) << 8) | \ - (((x) & 0x00FF0000) >> 8) | \ - (((x) & 0xFF000000) >> 24) ) -#undef EMAC -#undef EMAC_CTRL -#undef MDIO -#define EMAC ((struct tms570_emac *) (uintptr_t) 0xFCF78000) -#define EMAC_CTRL ((struct tms570_emac_ctrl *) (uintptr_t) 0xFCF78800) -#define MDIO ((struct tms570_mdio *) (uintptr_t) 0xFCF78900) -#define ETH_PKT_SIZE 1540 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] - __attribute__((section(".ETH_CPPI"), aligned(4))); // TX descriptors -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] - __attribute__((section(".ETH_CPPI"), aligned(4))); // RX descriptors -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] - __attribute__((aligned(4))); // RX ethernet buffers -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] - __attribute__((aligned(4))); // TX ethernet buffers -static struct mg_tcpip_if *s_ifp; // MIP interface -static uint16_t emac_read_phy(uint8_t addr, uint8_t reg) { - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; - MDIO->USERACCESS0 = MG_BIT(31) | ((reg & 0x1f) << 21) | - ((addr & 0x1f) << 16); - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; - return MDIO->USERACCESS0 & 0xffff; -} -static void emac_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; - MDIO->USERACCESS0 = MG_BIT(31) | MG_BIT(30) | ((reg & 0x1f) << 21) | - ((addr & 0x1f) << 16) | (val & 0xffff); - while(MDIO->USERACCESS0 & MG_BIT(31)) (void) 0; -} -static bool mg_tcpip_driver_tms570_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_tms570_data *d = - (struct mg_tcpip_driver_tms570_data *) ifp->driver_data; - s_ifp = ifp; - EMAC_CTRL->SOFTRESET = MG_BIT(0); // Reset the EMAC Control Module - while(EMAC_CTRL->SOFTRESET & MG_BIT(0)) (void) 0; // wait - EMAC->SOFTRESET = MG_BIT(0); // Reset the EMAC Module - while(EMAC->SOFTRESET & MG_BIT(0)) (void) 0; - EMAC->MACCONTROL = 0; - EMAC->RXCONTROL = 0; - EMAC->TXCONTROL = 0; - // Initialize all the header descriptor pointer registers - uint32_t i; - for(i = 0; i < ETH_DESC_CNT; i++) { - EMAC->RXHDP[i] = 0; - EMAC->TXHDP[i] = 0; - EMAC->RXCP[i] = 0; - EMAC->TXCP[i] = 0; - ///EMAC->RXFREEBUFFER[i] = 0xff; - } - // Clear the interrupt enable for all the channels - EMAC->TXINTMASKCLEAR = 0xff; - EMAC->RXINTMASKCLEAR = 0xff; - EMAC->MACHASH1 = 0; - EMAC->MACHASH2 = 0; - EMAC->RXBUFFEROFFSET = 0; - EMAC->RXUNICASTCLEAR = 0xff; - EMAC->RXUNICASTSET = 0; - EMAC->RXMBPENABLE = 0; - // init MDIO - // MDIO_CLK frequency = VCLK3/(CLKDIV + 1). (MDIO must be between 1.0 - 2.5Mhz) - uint32_t clkdiv = 75; // VCLK is configured to 75Mhz - // CLKDIV, ENABLE, PREAMBLE, FAULTENB - MDIO->CONTROL = (clkdiv - 1) | MG_BIT(30) | MG_BIT(20) | MG_BIT(18); - volatile int delay = 0xfff; - while (delay-- != 0) (void) 0; - struct mg_phy phy = {emac_read_phy, emac_write_phy}; - mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC); - // set the mac address - EMAC->MACSRCADDRHI = ifp->mac[0] | (ifp->mac[1] << 8) | (ifp->mac[2] << 16) | - (ifp->mac[3] << 24); - EMAC->MACSRCADDRLO = ifp->mac[4] | (ifp->mac[5] << 8); - uint32_t channel; - for (channel = 0; channel < 8; channel++) { - EMAC->MACINDEX = channel; - EMAC->MACADDRHI = ifp->mac[0] | (ifp->mac[1] << 8) | (ifp->mac[2] << 16) | - (ifp->mac[3] << 24); - EMAC->MACADDRLO = ifp->mac[4] | (ifp->mac[5] << 8) | MG_BIT(20) | - MG_BIT(19) | (channel << 16); - } - EMAC->RXUNICASTSET = 1; // accept unicast frames; - EMAC->RXMBPENABLE = MG_BIT(30) | MG_BIT(13); // CRC, broadcast; - - // Initialize the descriptors - for (i = 0; i < ETH_DESC_CNT; i++) { - if (i < ETH_DESC_CNT - 1) { - s_txdesc[i][0] = 0; - s_rxdesc[i][0] = SWAP32(((uint32_t) &s_rxdesc[i + 1][0])); - } - s_txdesc[i][1] = SWAP32(((uint32_t) s_txbuf[i])); - s_rxdesc[i][1] = SWAP32(((uint32_t) s_rxbuf[i])); - s_txdesc[i][2] = 0; - s_rxdesc[i][2] = SWAP32(ETH_PKT_SIZE); - s_txdesc[i][3] = 0; - s_rxdesc[i][3] = SWAP32(MG_BIT(29)); // OWN - } - s_txdesc[ETH_DESC_CNT - 1][0] = 0; - s_rxdesc[ETH_DESC_CNT - 1][0] = 0; - - EMAC->MACCONTROL = MG_BIT(5) | MG_BIT(0); // Enable MII, Full-duplex - //EMAC->TXINTMASKSET = 1; // Enable TX interrupt - EMAC->RXINTMASKSET = 1; // Enable RX interrupt - //EMAC_CTRL->C0TXEN = 1; // TX completion interrupt - EMAC_CTRL->C0RXEN = 1; // RX completion interrupt - EMAC->TXCONTROL = 1; // TXEN - EMAC->RXCONTROL = 1; // RXEN - EMAC->RXHDP[0] = (uint32_t) &s_rxdesc[0][0]; - return true; -} -static uint32_t s_txno; -static size_t mg_tcpip_driver_tms570_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // fail - } else if ((s_txdesc[s_txno][3] & SWAP32(MG_BIT(29)))) { - ifp->nerr++; - MG_ERROR(("No descriptors available")); - len = 0; // fail - } else { - memcpy(s_txbuf[s_txno], buf, len); // Copy data - if (len < 128) len = 128; - s_txdesc[s_txno][2] = SWAP32((uint32_t) len); // Set data len - s_txdesc[s_txno][3] = - SWAP32(MG_BIT(31) | MG_BIT(30) | MG_BIT(29) | len); // SOP, EOP, OWN, length - - while(EMAC->TXHDP[0] != 0) (void) 0; - EMAC->TXHDP[0] = (uint32_t) &s_txdesc[s_txno][0]; - if(++s_txno == ETH_DESC_CNT) { - s_txno = 0; - } - } - return len; - (void) ifp; -} -static bool mg_tcpip_driver_tms570_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_tms570_data *d = - (struct mg_tcpip_driver_tms570_data *) ifp->driver_data; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {emac_read_phy, emac_write_phy}; - up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { - // link state just went up - MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100, - full_duplex ? "full" : "half")); - } - return up; -} -#pragma CODE_STATE(EMAC_TX_IRQHandler, 32) -#pragma INTERRUPT(EMAC_TX_IRQHandler, IRQ) -void EMAC_TX_IRQHandler(void) { - uint32_t status = EMAC_CTRL->C0TXSTAT; - if (status & 1) { // interrupt caused on channel 0 - while(s_txdesc[s_txno][3] & SWAP32(MG_BIT(29))) (void) 0; - EMAC->TXCP[0] = (uint32_t) &s_txdesc[s_txno][0]; - } - //Write the DMA end of interrupt vector - EMAC->MACEOIVECTOR = 2; -} -static uint32_t s_rxno; -#pragma CODE_STATE(EMAC_RX_IRQHandler, 32) -#pragma INTERRUPT(EMAC_RX_IRQHandler, IRQ) -void EMAC_RX_IRQHandler(void) { - uint32_t status = EMAC_CTRL->C0RXSTAT; - if (status & 1) { // Frame received, loop - uint32_t i; - //MG_INFO(("RX interrupt")); - for (i = 0; i < 10; i++) { // read as they arrive but not forever - if ((s_rxdesc[s_rxno][3] & SWAP32(MG_BIT(29))) == 0) { - uint32_t len = SWAP32(s_rxdesc[s_rxno][3]) & 0xffff; - //MG_INFO(("recv len: %d", len)); - //mg_hexdump(s_rxbuf[s_rxno], len); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp); - uint32_t flags = s_rxdesc[s_rxno][3]; - s_rxdesc[s_rxno][3] = SWAP32(MG_BIT(29)); - s_rxdesc[s_rxno][2] = SWAP32(ETH_PKT_SIZE); - EMAC->RXCP[0] = (uint32_t) &s_rxdesc[s_rxno][0]; - if (flags & SWAP32(MG_BIT(28))) { - //MG_INFO(("EOQ detected")); - EMAC->RXHDP[0] = (uint32_t) &s_rxdesc[0][0]; - } - } - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - //Write the DMA end of interrupt vector - EMAC->MACEOIVECTOR = 1; -} -struct mg_tcpip_driver mg_tcpip_driver_tms570 = {mg_tcpip_driver_tms570_init, - mg_tcpip_driver_tms570_tx, NULL, - mg_tcpip_driver_tms570_up}; -#endif - - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/w5100.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5100) && MG_ENABLE_DRIVER_W5100 - -static void w5100_txn(struct mg_tcpip_spi *s, uint16_t addr, - bool wr, void *buf, size_t len) { - size_t i; - uint8_t *p = (uint8_t *) buf; - uint8_t control = wr ? 0xF0 : 0x0F; - uint8_t cmd[] = {control, (uint8_t) (addr >> 8), (uint8_t) (addr & 255)}; - s->begin(s->spi); - for (i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]); - for (i = 0; i < len; i++) { - uint8_t r = s->txn(s->spi, p[i]); - if (!wr) p[i] = r; - } - s->end(s->spi); -} - -// clang-format off -static void w5100_wn(struct mg_tcpip_spi *s, uint16_t addr, void *buf, size_t len) { w5100_txn(s, addr, true, buf, len); } -static void w5100_w1(struct mg_tcpip_spi *s, uint16_t addr, uint8_t val) { w5100_wn(s, addr, &val, 1); } -static void w5100_w2(struct mg_tcpip_spi *s, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5100_wn(s, addr, buf, sizeof(buf)); } -static void w5100_rn(struct mg_tcpip_spi *s, uint16_t addr, void *buf, size_t len) { w5100_txn(s, addr, false, buf, len); } -static uint8_t w5100_r1(struct mg_tcpip_spi *s, uint16_t addr) { uint8_t r = 0; w5100_rn(s, addr, &r, 1); return r; } -static uint16_t w5100_r2(struct mg_tcpip_spi *s, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5100_rn(s, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } -// clang-format on - -static size_t w5100_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len - while ((n2 = w5100_r2(s, 0x426)) > n) n = n2; // Until it is stable - if (n > 0) { - uint16_t ptr = w5100_r2(s, 0x428); // Get read pointer - if (n <= len + 2 && n > 1) { - r = (uint16_t) (n - 2); - } - uint16_t rxbuf_size = (1 << (w5100_r1(s, 0x1a) & 3)) * 1024; - uint16_t rxbuf_addr = 0x6000; - uint16_t ptr_ofs = (ptr + 2) & (rxbuf_size - 1); - if (ptr_ofs + r < rxbuf_size) { - w5100_rn(s, rxbuf_addr + ptr_ofs, buf, r); - } else { - uint16_t remaining_len = rxbuf_size - ptr_ofs; - w5100_rn(s, rxbuf_addr + ptr_ofs, buf, remaining_len); - w5100_rn(s, rxbuf_addr, buf + remaining_len, n - remaining_len); - } - w5100_w2(s, 0x428, (uint16_t) (ptr + n)); - w5100_w1(s, 0x401, 0x40); // Sock0 CR -> RECV - } - return r; -} - -static size_t w5100_tx(const void *buf, size_t buflen, - struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t i, n = 0, ptr = 0, len = (uint16_t) buflen; - while (n < len) n = w5100_r2(s, 0x420); // Wait for space - ptr = w5100_r2(s, 0x424); // Get write pointer - uint16_t txbuf_size = (1 << (w5100_r1(s, 0x1b) & 3)) * 1024; - uint16_t ptr_ofs = ptr & (txbuf_size - 1); - uint16_t txbuf_addr = 0x4000; - if (ptr_ofs + len > txbuf_size) { - uint16_t size = txbuf_size - ptr_ofs; - w5100_wn(s, txbuf_addr + ptr_ofs, (char*) buf, size); - w5100_wn(s, txbuf_addr, (char*) buf + size, len - size); - } else { - w5100_wn(s, txbuf_addr + ptr_ofs, (char*) buf, len); - } - w5100_w2(s, 0x424, (uint16_t) (ptr + len)); // Advance write pointer - w5100_w1(s, 0x401, 0x20); // Sock0 CR -> SEND - for (i = 0; i < 40; i++) { - uint8_t ir = w5100_r1(s, 0x402); // Read S0 IR - if (ir == 0) continue; - // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr); - w5100_w1(s, 0x402, ir); // Write S0 IR: clear it! - if (ir & 8) len = 0; // Timeout. Report error - if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout - } - return len; -} - -static bool w5100_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - s->end(s->spi); - w5100_w1(s, 0, 0x80); // Reset chip: CR -> 0x80 - w5100_w1(s, 0x72, 0x53); // CR PHYLCKR -> unlock PHY - w5100_w1(s, 0x46, 0); // CR PHYCR0 -> autonegotiation - w5100_w1(s, 0x47, 0); // CR PHYCR1 -> reset - w5100_w1(s, 0x72, 0x00); // CR PHYLCKR -> lock PHY - w5100_w1(s, 0x1a, 6); // Sock0 RX buf size - 4KB - w5100_w1(s, 0x1b, 6); // Sock0 TX buf size - 4KB - w5100_w1(s, 0x400, 4); // Sock0 MR -> MACRAW - w5100_w1(s, 0x401, 1); // Sock0 CR -> OPEN - return w5100_r1(s, 0x403) == 0x42; // Sock0 SR == MACRAW -} - -static bool w5100_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data; - uint8_t physr0 = w5100_r1(spi, 0x3c); - return physr0 & 1; // Bit 0 of PHYSR is LNK (0 - down, 1 - up) -} - -struct mg_tcpip_driver mg_tcpip_driver_w5100 = {w5100_init, w5100_tx, w5100_rx, - w5100_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/w5500.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_W5500) && MG_ENABLE_DRIVER_W5500 - -enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 }; - -static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, - bool wr, void *buf, size_t len) { - size_t i; - uint8_t *p = (uint8_t *) buf; - uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255), - (uint8_t) ((block << 3) | (wr ? 4 : 0))}; - s->begin(s->spi); - for (i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]); - for (i = 0; i < len; i++) { - uint8_t r = s->txn(s->spi, p[i]); - if (!wr) p[i] = r; - } - s->end(s->spi); -} - -// clang-format off -static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); } -static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); } -static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); } -static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); } -static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; } -static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } -// clang-format on - -static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len - while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable - // printf("RSR: %d\n", (int) n); - if (n > 0) { - uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer - n = w5500_r2(s, W5500_RX0, ptr); // Read frame length - if (n <= len + 2 && n > 1) { - r = (uint16_t) (n - 2); - w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r); - } - w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer - w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV - // printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r); - } - return r; -} - -static size_t w5500_tx(const void *buf, size_t buflen, - struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - uint16_t i, ptr, n = 0, len = (uint16_t) buflen; - while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space - ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer - w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data - w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer - w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND - for (i = 0; i < 40; i++) { - uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR - if (ir == 0) continue; - // printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr); - w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it! - if (ir & 8) len = 0; // Timeout. Report error - if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout - } - return len; -} - -static bool w5500_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data; - s->end(s->spi); - w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80 - w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset - w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set - // w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC - w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size - w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size - w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW - w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN - return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW -} - -static bool w5500_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data; - uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e); - return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up) -} - -struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, - w5500_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/xmc.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC - -struct ETH_GLOBAL_TypeDef { - volatile uint32_t MAC_CONFIGURATION, MAC_FRAME_FILTER, HASH_TABLE_HIGH, - HASH_TABLE_LOW, GMII_ADDRESS, GMII_DATA, FLOW_CONTROL, VLAN_TAG, VERSION, - DEBUG, REMOTE_WAKE_UP_FRAME_FILTER, PMT_CONTROL_STATUS, RESERVED[2], - INTERRUPT_STATUS, INTERRUPT_MASK, MAC_ADDRESS0_HIGH, MAC_ADDRESS0_LOW, - MAC_ADDRESS1_HIGH, MAC_ADDRESS1_LOW, MAC_ADDRESS2_HIGH, MAC_ADDRESS2_LOW, - MAC_ADDRESS3_HIGH, MAC_ADDRESS3_LOW, RESERVED1[40], MMC_CONTROL, - MMC_RECEIVE_INTERRUPT, MMC_TRANSMIT_INTERRUPT, MMC_RECEIVE_INTERRUPT_MASK, - MMC_TRANSMIT_INTERRUPT_MASK, TX_STATISTICS[26], RESERVED2, - RX_STATISTICS_1[26], RESERVED3[6], MMC_IPC_RECEIVE_INTERRUPT_MASK, - RESERVED4, MMC_IPC_RECEIVE_INTERRUPT, RESERVED5, RX_STATISTICS_2[30], - RESERVED7[286], TIMESTAMP_CONTROL, SUB_SECOND_INCREMENT, - SYSTEM_TIME_SECONDS, SYSTEM_TIME_NANOSECONDS, - SYSTEM_TIME_SECONDS_UPDATE, SYSTEM_TIME_NANOSECONDS_UPDATE, - TIMESTAMP_ADDEND, TARGET_TIME_SECONDS, TARGET_TIME_NANOSECONDS, - SYSTEM_TIME_HIGHER_WORD_SECONDS, TIMESTAMP_STATUS, - PPS_CONTROL, RESERVED8[564], BUS_MODE, TRANSMIT_POLL_DEMAND, - RECEIVE_POLL_DEMAND, RECEIVE_DESCRIPTOR_LIST_ADDRESS, - TRANSMIT_DESCRIPTOR_LIST_ADDRESS, STATUS, OPERATION_MODE, - INTERRUPT_ENABLE, MISSED_FRAME_AND_BUFFER_OVERFLOW_COUNTER, - RECEIVE_INTERRUPT_WATCHDOG_TIMER, RESERVED9, AHB_STATUS, - RESERVED10[6], CURRENT_HOST_TRANSMIT_DESCRIPTOR, - CURRENT_HOST_RECEIVE_DESCRIPTOR, CURRENT_HOST_TRANSMIT_BUFFER_ADDRESS, - CURRENT_HOST_RECEIVE_BUFFER_ADDRESS, HW_FEATURE; -}; - -#undef ETH0 -#define ETH0 ((struct ETH_GLOBAL_TypeDef*) 0x5000C000UL) - -#define ETH_PKT_SIZE 1536 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 4 // Descriptor size (words) - -#ifndef ETH_RAM_SECTION -// if no section is specified, then the data will be placed in the default -// bss section -#define ETH_RAM_SECTION -#endif - -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] ETH_RAM_SECTION; -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] ETH_RAM_SECTION; -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] ETH_RAM_SECTION; // RX descriptors -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] ETH_RAM_SECTION; // TX descriptors -static uint8_t s_txno; // Current TX descriptor -static uint8_t s_rxno; // Current RX descriptor - -static struct mg_tcpip_if *s_ifp; // MIP interface -enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 }; - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) | - ((uint32_t)addr << 11) | - ((uint32_t)reg << 6) | 1; - while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0; - return (uint16_t)(ETH0->GMII_DATA & 0xffff); -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH0->GMII_DATA = val; - ETH0->GMII_ADDRESS = (ETH0->GMII_ADDRESS & 0x3c) | - ((uint32_t)addr << 11) | - ((uint32_t)reg << 6) | 3; - while ((ETH0->GMII_ADDRESS & 1) != 0) (void) 0; -} - -static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc_data *d) { - if (d->mdc_cr == -1) { - // assume ETH clock is 60MHz by default - // then according to 13.2.8.1, we need to set value 3 - return 3; - } - - return d->mdc_cr; -} - -static bool mg_tcpip_driver_xmc_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_xmc_data *d = - (struct mg_tcpip_driver_xmc_data *) ifp->driver_data; - s_ifp = ifp; - - // reset MAC - ETH0->BUS_MODE |= 1; - while (ETH0->BUS_MODE & 1) (void) 0; - - // set clock rate - ETH0->GMII_ADDRESS = get_clock_rate(d) << 2; - - // init phy - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC); - - // configure MAC: DO, DM, FES, TC - ETH0->MAC_CONFIGURATION = MG_BIT(13) | MG_BIT(11) | MG_BIT(14) | MG_BIT(24); - - // set the MAC address - ETH0->MAC_ADDRESS0_HIGH = MG_U32(0, 0, ifp->mac[5], ifp->mac[4]); - ETH0->MAC_ADDRESS0_LOW = - MG_U32(ifp->mac[3], ifp->mac[2], ifp->mac[1], ifp->mac[0]); - - // Configure the receive filter - ETH0->MAC_FRAME_FILTER = MG_BIT(10) | MG_BIT(2); // HFP, HMC - // Disable flow control - ETH0->FLOW_CONTROL = 0; - // Enable store and forward mode - ETH0->OPERATION_MODE = MG_BIT(25) | MG_BIT(21); // RSF, TSF - - // Configure DMA bus mode (AAL, USP, RPBL, PBL) - ETH0->BUS_MODE = MG_BIT(25) | MG_BIT(23) | (32 << 17) | (32 << 8); - - // init RX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = MG_BIT(31); // OWN descriptor - s_rxdesc[i][1] = MG_BIT(14) | ETH_PKT_SIZE; - s_rxdesc[i][2] = (uint32_t) s_rxbuf[i]; - if (i == ETH_DESC_CNT - 1) { - s_rxdesc[i][3] = (uint32_t) &s_rxdesc[0][0]; - } else { - s_rxdesc[i][3] = (uint32_t) &s_rxdesc[i + 1][0]; - } - } - ETH0->RECEIVE_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_rxdesc[0][0]; - - // init TX descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_txdesc[i][0] = MG_BIT(30) | MG_BIT(20); - s_txdesc[i][2] = (uint32_t) s_txbuf[i]; - if (i == ETH_DESC_CNT - 1) { - s_txdesc[i][3] = (uint32_t) &s_txdesc[0][0]; - } else { - s_txdesc[i][3] = (uint32_t) &s_txdesc[i + 1][0]; - } - } - ETH0->TRANSMIT_DESCRIPTOR_LIST_ADDRESS = (uint32_t) &s_txdesc[0][0]; - - // Clear interrupts - ETH0->STATUS = 0xFFFFFFFF; - - // Disable MAC interrupts - ETH0->MMC_TRANSMIT_INTERRUPT_MASK = 0xFFFFFFFF; - ETH0->MMC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF; - ETH0->MMC_IPC_RECEIVE_INTERRUPT_MASK = 0xFFFFFFFF; - ETH0->INTERRUPT_MASK = MG_BIT(9) | MG_BIT(3); // TSIM, PMTIM - - //Enable interrupts (NIE, RIE, TIE) - ETH0->INTERRUPT_ENABLE = MG_BIT(16) | MG_BIT(6) | MG_BIT(0); - - // Enable MAC transmission and reception (TE, RE) - ETH0->MAC_CONFIGURATION |= MG_BIT(3) | MG_BIT(2); - // Enable DMA transmission and reception (ST, SR) - ETH0->OPERATION_MODE |= MG_BIT(13) | MG_BIT(1); - return true; -} - -static size_t mg_tcpip_driver_xmc_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if ((s_txdesc[s_txno][0] & MG_BIT(31))) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); - s_txdesc[s_txno][1] = len; - // Table 13-19 Transmit Descriptor Word 0 (IC, LS, FS, TCH) - s_txdesc[s_txno][0] = MG_BIT(30) | MG_BIT(29) | MG_BIT(28) | MG_BIT(20); - s_txdesc[s_txno][0] |= MG_BIT(31); // OWN bit: handle control to DMA - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - - // Resume processing - ETH0->STATUS = MG_BIT(2); // clear Transmit unavailable - ETH0->TRANSMIT_POLL_DEMAND = 0; - return len; -} - -static bool mg_tcpip_driver_xmc_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_xmc_data *d = - (struct mg_tcpip_driver_xmc_data *) ifp->driver_data; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - MG_DEBUG(("Link is %uM %s-duplex", speed == MG_PHY_SPEED_10M ? 10 : 100, - full_duplex ? "full" : "half")); - } - return up; -} - -void ETH0_0_IRQHandler(void); -void ETH0_0_IRQHandler(void) { - uint32_t irq_status = ETH0->STATUS; - - // check if a frame was received - if (irq_status & MG_BIT(6)) { - for (uint8_t i = 0; i < ETH_DESC_CNT; i++) { - if ((s_rxdesc[s_rxno][0] & MG_BIT(31)) == 0) { - size_t len = (s_rxdesc[s_rxno][0] & 0x3fff0000) >> 16; - mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); - s_rxdesc[s_rxno][0] = MG_BIT(31); // OWN bit: handle control to DMA - // Resume processing - ETH0->STATUS = MG_BIT(7) | MG_BIT(6); // clear RU and RI - ETH0->RECEIVE_POLL_DEMAND = 0; - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - ETH0->STATUS = MG_BIT(6); - } - - // clear Successful transmission interrupt - if (irq_status & 1) { - ETH0->STATUS = 1; - } - - // clear normal interrupt - if (irq_status & MG_BIT(16)) { - ETH0->STATUS = MG_BIT(16); - } -} - -struct mg_tcpip_driver mg_tcpip_driver_xmc = { - mg_tcpip_driver_xmc_init, mg_tcpip_driver_xmc_tx, NULL, - mg_tcpip_driver_xmc_up}; -#endif - -#ifdef MG_ENABLE_LINES -#line 1 "src/drivers/xmc7.c" -#endif - - -#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7 - -struct ETH_Type { - volatile uint32_t CTL, STATUS, RESERVED[1022], NETWORK_CONTROL, - NETWORK_CONFIG, NETWORK_STATUS, USER_IO_REGISTER, DMA_CONFIG, - TRANSMIT_STATUS, RECEIVE_Q_PTR, TRANSMIT_Q_PTR, RECEIVE_STATUS, - INT_STATUS, INT_ENABLE, INT_DISABLE, INT_MASK, PHY_MANAGEMENT, PAUSE_TIME, - TX_PAUSE_QUANTUM, PBUF_TXCUTTHRU, PBUF_RXCUTTHRU, JUMBO_MAX_LENGTH, - EXTERNAL_FIFO_INTERFACE, RESERVED1, AXI_MAX_PIPELINE, RSC_CONTROL, - INT_MODERATION, SYS_WAKE_TIME, RESERVED2[7], HASH_BOTTOM, HASH_TOP, - SPEC_ADD1_BOTTOM, SPEC_ADD1_TOP, SPEC_ADD2_BOTTOM, SPEC_ADD2_TOP, - SPEC_ADD3_BOTTOM, SPEC_ADD3_TOP, SPEC_ADD4_BOTTOM, SPEC_ADD4_TOP, - SPEC_TYPE1, SPEC_TYPE2, SPEC_TYPE3, SPEC_TYPE4, WOL_REGISTER, - STRETCH_RATIO, STACKED_VLAN, TX_PFC_PAUSE, MASK_ADD1_BOTTOM, - MASK_ADD1_TOP, DMA_ADDR_OR_MASK, RX_PTP_UNICAST, TX_PTP_UNICAST, - TSU_NSEC_CMP, TSU_SEC_CMP, TSU_MSB_SEC_CMP, TSU_PTP_TX_MSB_SEC, - TSU_PTP_RX_MSB_SEC, TSU_PEER_TX_MSB_SEC, TSU_PEER_RX_MSB_SEC, - DPRAM_FILL_DBG, REVISION_REG, OCTETS_TXED_BOTTOM, OCTETS_TXED_TOP, - FRAMES_TXED_OK, BROADCAST_TXED, MULTICAST_TXED, PAUSE_FRAMES_TXED, - FRAMES_TXED_64, FRAMES_TXED_65, FRAMES_TXED_128, FRAMES_TXED_256, - FRAMES_TXED_512, FRAMES_TXED_1024, FRAMES_TXED_1519, TX_UNDERRUNS, - SINGLE_COLLISIONS, MULTIPLE_COLLISIONS, EXCESSIVE_COLLISIONS, - LATE_COLLISIONS, DEFERRED_FRAMES, CRS_ERRORS, OCTETS_RXED_BOTTOM, - OCTETS_RXED_TOP, FRAMES_RXED_OK, BROADCAST_RXED, MULTICAST_RXED, - PAUSE_FRAMES_RXED, FRAMES_RXED_64, FRAMES_RXED_65, FRAMES_RXED_128, - FRAMES_RXED_256, FRAMES_RXED_512, FRAMES_RXED_1024, FRAMES_RXED_1519, - UNDERSIZE_FRAMES, EXCESSIVE_RX_LENGTH, RX_JABBERS, FCS_ERRORS, - RX_LENGTH_ERRORS, RX_SYMBOL_ERRORS, ALIGNMENT_ERRORS, RX_RESOURCE_ERRORS, - RX_OVERRUNS, RX_IP_CK_ERRORS, RX_TCP_CK_ERRORS, RX_UDP_CK_ERRORS, - AUTO_FLUSHED_PKTS, RESERVED3, TSU_TIMER_INCR_SUB_NSEC, TSU_TIMER_MSB_SEC, - TSU_STROBE_MSB_SEC, TSU_STROBE_SEC, TSU_STROBE_NSEC, TSU_TIMER_SEC, - TSU_TIMER_NSEC, TSU_TIMER_ADJUST, TSU_TIMER_INCR, TSU_PTP_TX_SEC, - TSU_PTP_TX_NSEC, TSU_PTP_RX_SEC, TSU_PTP_RX_NSEC, TSU_PEER_TX_SEC, - TSU_PEER_TX_NSEC, TSU_PEER_RX_SEC, TSU_PEER_RX_NSEC, PCS_CONTROL, - PCS_STATUS, RESERVED4[2], PCS_AN_ADV, PCS_AN_LP_BASE, PCS_AN_EXP, - PCS_AN_NP_TX, PCS_AN_LP_NP, RESERVED5[6], PCS_AN_EXT_STATUS, RESERVED6[8], - TX_PAUSE_QUANTUM1, TX_PAUSE_QUANTUM2, TX_PAUSE_QUANTUM3, RESERVED7, - RX_LPI, RX_LPI_TIME, TX_LPI, TX_LPI_TIME, DESIGNCFG_DEBUG1, - DESIGNCFG_DEBUG2, DESIGNCFG_DEBUG3, DESIGNCFG_DEBUG4, DESIGNCFG_DEBUG5, - DESIGNCFG_DEBUG6, DESIGNCFG_DEBUG7, DESIGNCFG_DEBUG8, DESIGNCFG_DEBUG9, - DESIGNCFG_DEBUG10, RESERVED8[22], SPEC_ADD5_BOTTOM, SPEC_ADD5_TOP, - RESERVED9[60], SPEC_ADD36_BOTTOM, SPEC_ADD36_TOP, INT_Q1_STATUS, - INT_Q2_STATUS, INT_Q3_STATUS, RESERVED10[11], INT_Q15_STATUS, RESERVED11, - TRANSMIT_Q1_PTR, TRANSMIT_Q2_PTR, TRANSMIT_Q3_PTR, RESERVED12[11], - TRANSMIT_Q15_PTR, RESERVED13, RECEIVE_Q1_PTR, RECEIVE_Q2_PTR, - RECEIVE_Q3_PTR, RESERVED14[3], RECEIVE_Q7_PTR, RESERVED15, - DMA_RXBUF_SIZE_Q1, DMA_RXBUF_SIZE_Q2, DMA_RXBUF_SIZE_Q3, RESERVED16[3], - DMA_RXBUF_SIZE_Q7, CBS_CONTROL, CBS_IDLESLOPE_Q_A, CBS_IDLESLOPE_Q_B, - UPPER_TX_Q_BASE_ADDR, TX_BD_CONTROL, RX_BD_CONTROL, UPPER_RX_Q_BASE_ADDR, - RESERVED17[2], HIDDEN_REG0, HIDDEN_REG1, HIDDEN_REG2, HIDDEN_REG3, - RESERVED18[2], HIDDEN_REG4, HIDDEN_REG5; -}; - -#define ETH0 ((struct ETH_Type *) 0x40490000) - -#define ETH_PKT_SIZE 1536 // Max frame size -#define ETH_DESC_CNT 4 // Descriptors count -#define ETH_DS 2 // Descriptor size (words) - -// TODO(): handle these in a portable compiler-independent CMSIS-friendly way -#define MG_8BYTE_ALIGNED __attribute__((aligned((8U)))) - -static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; -static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; -static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED; -static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS] MG_8BYTE_ALIGNED; -static uint8_t s_txno MG_8BYTE_ALIGNED; // Current TX descriptor -static uint8_t s_rxno MG_8BYTE_ALIGNED; // Current RX descriptor - -static struct mg_tcpip_if *s_ifp; // MIP interface -enum { MG_PHY_ADDR = 0, MG_PHYREG_BCR = 0, MG_PHYREG_BSR = 1 }; - -static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) { - // WRITE1, READ OPERATION, PHY, REG, WRITE10 - ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(29) | ((addr & 0xf) << 24) | - ((reg & 0x1f) << 18) | MG_BIT(17); - while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0; - return ETH0->PHY_MANAGEMENT & 0xffff; -} - -static void eth_write_phy(uint8_t addr, uint8_t reg, uint16_t val) { - ETH0->PHY_MANAGEMENT = MG_BIT(30) | MG_BIT(28) | ((addr & 0xf) << 24) | - ((reg & 0x1f) << 18) | MG_BIT(17) | val; - while ((ETH0->NETWORK_STATUS & MG_BIT(2)) == 0) (void) 0; -} - -static uint32_t get_clock_rate(struct mg_tcpip_driver_xmc7_data *d) { - // see ETH0 -> NETWORK_CONFIG register - (void) d; - return 3; -} - -static bool mg_tcpip_driver_xmc7_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_xmc7_data *d = - (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data; - s_ifp = ifp; - - // enable controller, set RGMII mode - ETH0->CTL = MG_BIT(31) | (4 << 8) | 2; - - uint32_t cr = get_clock_rate(d); - // set NSP change, ignore RX FCS, data bus width, clock rate - // frame length 1536, full duplex, speed - ETH0->NETWORK_CONFIG = MG_BIT(29) | MG_BIT(26) | MG_BIT(21) | - ((cr & 7) << 18) | MG_BIT(8) | MG_BIT(4) | MG_BIT(1) | - MG_BIT(0); - - // config DMA settings: Force TX burst, Discard on Error, set RX buffer size - // to 1536, TX_PBUF_SIZE, RX_PBUF_SIZE, AMBA_BURST_LENGTH - ETH0->DMA_CONFIG = - MG_BIT(26) | MG_BIT(24) | (0x18 << 16) | MG_BIT(10) | (3 << 8) | 4; - - // initialize descriptors - for (int i = 0; i < ETH_DESC_CNT; i++) { - s_rxdesc[i][0] = (uint32_t) s_rxbuf[i]; - if (i == ETH_DESC_CNT - 1) { - s_rxdesc[i][0] |= MG_BIT(1); // mark last descriptor - } - - s_txdesc[i][0] = (uint32_t) s_txbuf[i]; - s_txdesc[i][1] = MG_BIT(31); // OWN descriptor - if (i == ETH_DESC_CNT - 1) { - s_txdesc[i][1] |= MG_BIT(30); // mark last descriptor - } - } - ETH0->RECEIVE_Q_PTR = (uint32_t) s_rxdesc; - ETH0->TRANSMIT_Q_PTR = (uint32_t) s_txdesc; - - // disable other queues - ETH0->TRANSMIT_Q2_PTR = 1; - ETH0->TRANSMIT_Q1_PTR = 1; - ETH0->RECEIVE_Q2_PTR = 1; - ETH0->RECEIVE_Q1_PTR = 1; - - // enable interrupts (RX complete) - ETH0->INT_ENABLE = MG_BIT(1); - - // set MAC address - ETH0->SPEC_ADD1_BOTTOM = - ifp->mac[3] << 24 | ifp->mac[2] << 16 | ifp->mac[1] << 8 | ifp->mac[0]; - ETH0->SPEC_ADD1_TOP = ifp->mac[5] << 8 | ifp->mac[4]; - - // enable MDIO, TX, RX - ETH0->NETWORK_CONTROL = MG_BIT(4) | MG_BIT(3) | MG_BIT(2); - - // start transmission - ETH0->NETWORK_CONTROL |= MG_BIT(9); - - // init phy - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - mg_phy_init(&phy, d->phy_addr, MG_PHY_CLOCKS_MAC); - - (void) d; - return true; -} - -static size_t mg_tcpip_driver_xmc7_tx(const void *buf, size_t len, - struct mg_tcpip_if *ifp) { - if (len > sizeof(s_txbuf[s_txno])) { - MG_ERROR(("Frame too big, %ld", (long) len)); - len = 0; // Frame is too big - } else if (((s_txdesc[s_txno][1] & MG_BIT(31)) == 0)) { - ifp->nerr++; - MG_ERROR(("No free descriptors")); - len = 0; // All descriptors are busy, fail - } else { - memcpy(s_txbuf[s_txno], buf, len); - s_txdesc[s_txno][1] = (s_txno == ETH_DESC_CNT - 1 ? MG_BIT(30) : 0) | - MG_BIT(15) | len; // Last buffer and length - - ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission - if (++s_txno >= ETH_DESC_CNT) s_txno = 0; - } - - MG_DSB(); - ETH0->TRANSMIT_STATUS = ETH0->TRANSMIT_STATUS; - ETH0->NETWORK_CONTROL |= MG_BIT(9); // enable transmission - - return len; -} - -static bool mg_tcpip_driver_xmc7_up(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_xmc7_data *d = - (struct mg_tcpip_driver_xmc7_data *) ifp->driver_data; - uint8_t speed = MG_PHY_SPEED_10M; - bool up = false, full_duplex = false; - struct mg_phy phy = {eth_read_phy, eth_write_phy}; - up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed); - if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up - // tmp = reg with flags set to the most likely situation: 100M full-duplex - // if(link is slow or half) set flags otherwise - // reg = tmp - uint32_t netconf = ETH0->NETWORK_CONFIG; - MG_SET_BITS(netconf, MG_BIT(10), - MG_BIT(1) | MG_BIT(0)); // 100M, Full-duplex - uint32_t ctl = ETH0->CTL; - MG_SET_BITS(ctl, 0xFF00, 4 << 8); // /5 for 25M clock - if (speed == MG_PHY_SPEED_1000M) { - netconf |= MG_BIT(10); // 1000M - MG_SET_BITS(ctl, 0xFF00, 0); // /1 for 125M clock TODO() IS THIS NEEDED ? - } else if (speed == MG_PHY_SPEED_10M) { - netconf &= ~MG_BIT(0); // 10M - MG_SET_BITS(ctl, 0xFF00, 49); // /50 for 2.5M clock - } - if (full_duplex == false) netconf &= ~MG_BIT(1); // Half-duplex - ETH0->NETWORK_CONFIG = netconf; // IRQ handler does not fiddle with these - ETH0->CTL = ctl; - MG_DEBUG(("Link is %uM %s-duplex", - speed == MG_PHY_SPEED_10M - ? 10 - : (speed == MG_PHY_SPEED_100M ? 100 : 1000), - full_duplex ? "full" : "half")); - } - return up; -} - -void ETH_IRQHandler(void) { - uint32_t irq_status = ETH0->INT_STATUS; - if (irq_status & MG_BIT(1)) { - for (uint8_t i = 0; i < ETH_DESC_CNT; i++) { - if (s_rxdesc[s_rxno][0] & MG_BIT(0)) { - size_t len = s_rxdesc[s_rxno][1] & (MG_BIT(13) - 1); - mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp); - s_rxdesc[s_rxno][0] &= ~MG_BIT(0); // OWN bit: handle control to DMA - if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; - } - } - } - - ETH0->INT_STATUS = irq_status; -} - -struct mg_tcpip_driver mg_tcpip_driver_xmc7 = {mg_tcpip_driver_xmc7_init, - mg_tcpip_driver_xmc7_tx, NULL, - mg_tcpip_driver_xmc7_up}; -#endif diff --git a/test/tls_multirec/thefile b/test/tls_multirec/thefile deleted file mode 120000 index d0b0e8e0..00000000 --- a/test/tls_multirec/thefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/test/tls_multirec/tls_multirec.diff b/test/tls_multirec/tls_multirec.diff deleted file mode 100644 index 961e0631..00000000 --- a/test/tls_multirec/tls_multirec.diff +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/mongoose.c b/mongoose.c -index 44539e81..b3f1c83a 100644 ---- a/mongoose.c -+++ b/mongoose.c -@@ -11300,9 +11300,16 @@ void mg_tls_free(struct mg_connection *c) { - long mg_tls_send(struct mg_connection *c, const void *buf, size_t len) { - struct tls_data *tls = (struct tls_data *) c->tls; - long n = MG_IO_WAIT; -+ size_t maxsize = 256, encrypted = 0; - if (len > MG_IO_SIZE) len = MG_IO_SIZE; - if (len > 16384) len = 16384; -- mg_tls_encrypt(c, (const uint8_t *) buf, len, MG_TLS_APP_DATA); -+ while (encrypted < len) { -+ const uint8_t *chunk = (const uint8_t *) buf + encrypted; -+ size_t chunksize = len - encrypted; -+ if (chunksize > maxsize) chunksize = maxsize; -+ mg_tls_encrypt(c, chunk, chunksize, MG_TLS_APP_DATA); -+ encrypted += chunksize; -+ } - while (tls->send.len > 0 && - (n = mg_io_send(c, tls->send.buf, tls->send.len)) > 0) { - mg_iobuf_del(&tls->send, 0, (size_t) n); diff --git a/test/unit_test.c b/test/unit_test.c index 3f0660c5..a444816c 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -925,6 +925,10 @@ static void fcb(struct mg_connection *c, int ev, void *ev_data) { } } +// Max number of mg_mgr_poll() calls fetch() waits for a response. Large +// transfers over builtin TLS with small records (MG_TLS_RECORD_SIZE) need more +static int s_fetch_polls = 100; + static int fetch(struct mg_mgr *mgr, char *buf, const char *url, const char *fmt, ...) { struct fetch_data fd = {buf, 0, 0}; @@ -953,7 +957,7 @@ static int fetch(struct mg_mgr *mgr, char *buf, const char *url, mg_vprintf(c, fmt, &ap); va_end(ap); buf[0] = '\0'; - for (i = 0; i < 100 && buf[0] == '\0'; i++) mg_mgr_poll(mgr, 1); + for (i = 0; i < s_fetch_polls && buf[0] == '\0'; i++) mg_mgr_poll(mgr, 1); if (!fd.closed) c->is_closing = 1; mg_mgr_poll(mgr, 1); return fd.code; @@ -1522,7 +1526,6 @@ static void test_http_404(void) { } static void test_tls(void) { -// return; #if MG_TLS struct mg_mgr mgr; struct mg_connection *c; @@ -1545,6 +1548,7 @@ static void test_tls(void) { // POST a large file, several MSS but less than max TLS record length // make sure we drain TLS buffers and read all, #2619 ASSERT(data.len > 3100 && data.len < 16384); // pick another file on failure + s_fetch_polls = 5000; // Many small TLS records, one is decrypted per poll ASSERT(fetch(&mgr, buf, url, "POST /body HTTP/1.0\n" "Content-Length: %lu\n\n" @@ -1561,29 +1565,7 @@ static void test_tls(void) { "%s", bd.len, bd.buf) == 200); ASSERT(cmpbody(buf, bd.buf) == 0); -#if MG_TLS == MG_TLS_BUILTIN && defined(__linux__) && \ - MG_ENABLE_CHACHA20 // skip for non-CHACHA tests - // fire patched server, test multiple TLS records per TCP segment handling - // skip other TLS stacks to avoid "bad client hello", we are 1.3 only - if (access("tls_multirec/server", X_OK) == 0) { - ASSERT(system("tls_multirec/server -d tls_multirec &") == 0); - sleep(1); - // fetch() needs to loop enough times in order to process all TLS records; - // otherwise it will end with 200 and shorter file contents - ASSERT(fetch(&mgr, buf, "https://localhost:8443", - "GET /thefile HTTP/1.0\n\n") == 200); - data = mg_unpacked("/Makefile"); - ASSERT(cmpbody(buf, data.buf) == 0); // "thefile" links to Makefile - ASSERT(system("killall tls_multirec/server") == 0); - } else { - printf("\nSKIPPED TLS MULTIPLE RECORDS TEST, tls_multirec/server NOT PRESENT\n"); - } -#else - printf( - "\n Skipping multiple TLS records per TCP segment handling test, server " - "is 1.3 only; re-enable when other stacks can be easily configured for " - "1.3\n"); -#endif + s_fetch_polls = 100; // Repeat the simplest test with two-way authentication opts.ca = mg_unpacked("/certs/ca.crt"); // configure the server for two-way @@ -3394,7 +3376,7 @@ static void test_http_chunked_case(mg_event_handler_t s, mg_event_handler_t c, struct mg_mgr mgr; uint32_t i, crc = 0, expected_crc = mg_crc32(0, expected, strlen(expected)); struct mg_connection *conn; - static uint16_t port = 32344; // To prevent bind errors on Windows + static uint16_t port = 32344; // To prevent bind errors on Windows mg_snprintf(url, sizeof(url), "http://127.0.0.1:%d", port++); mg_mgr_init(&mgr); mg_http_listen(&mgr, url, s, NULL); @@ -4424,18 +4406,39 @@ static void test_split(void) { ASSERT(mg_strcmp(b, mg_str("")) == 0); } -static void test_x25519(void) { +// RFC 6979 A.2.5: P-256, SHA-256, message "sample" +static void test_p256(void) { #if MG_TLS == MG_TLS_BUILTIN - uint8_t key[X25519_BYTES]; - uint8_t buf[X25519_BYTES]; - char tmp[100]; - size_t i; - for (i = 0; i < sizeof(key); i++) key[i] = (uint8_t) i; - for (i = 0; i < sizeof(buf); i++) buf[i] = 0; - mg_tls_x25519(buf, key, X25519_BASE_POINT, 1); - mg_snprintf(tmp, sizeof(tmp), "%M", mg_print_hex, sizeof(buf), buf); - MG_INFO(("%s", tmp)); - ASSERT(mg_strcmp(mg_str("8f40c5adb6"), mg_str_n(tmp, 10)) == 0); + static const uint8_t x[32] = { + 0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, + 0x57, 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, + 0x9b, 0x12, 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21}; + static const uint8_t ux[8] = {0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31}; + static const uint8_t uy[8] = {0x79, 0x03, 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99}; + static const uint8_t rs[64] = { + 0xef, 0xd4, 0x8b, 0x2a, 0xac, 0xb6, 0xa8, 0xfd, 0x11, 0x40, 0xdd, 0x9c, + 0xd4, 0x5e, 0x81, 0xd6, 0x9d, 0x2c, 0x87, 0x7b, 0x56, 0xaa, 0xf9, 0x91, + 0xc3, 0x4d, 0x0e, 0xa8, 0x4e, 0xaf, 0x37, 0x16, 0xf7, 0xcb, 0x1c, 0x94, + 0x2d, 0x65, 0x7c, 0x41, 0xd4, 0x36, 0xc7, 0xa1, 0xb6, 0xe2, 0x9f, 0x65, + 0xf3, 0xe9, 0x00, 0xdb, 0xb9, 0xaf, 0xf4, 0x06, 0x4d, 0xc4, 0xab, 0x2f, + 0x84, 0x3a, 0xcd, 0xa8}; + uint8_t pub[64], hash[32], sig[64], prv2[32], pub2[64], sec1[32], sec2[32]; + mg_sha256(hash, (uint8_t *) "sample", 6); + ASSERT(mg_p256_compute_public_key(x, pub) == 1); + ASSERT(memcmp(pub, ux, sizeof(ux)) == 0); + ASSERT(memcmp(pub + 32, uy, sizeof(uy)) == 0); + ASSERT(mg_p256_sign(x, hash, sizeof(hash), sig) == 1); + ASSERT(memcmp(sig, rs, sizeof(rs)) == 0); + ASSERT(mg_p256_verify(pub, hash, sizeof(hash), sig) == 1); + sig[10] ^= 1; + ASSERT(mg_p256_verify(pub, hash, sizeof(hash), sig) == 0); + // ECDH: both sides compute the same secret + ASSERT(mg_p256_make_key(pub2, prv2) == 1); + ASSERT(mg_p256_shared_secret(pub2, x, sec1) == 1); + ASSERT(mg_p256_shared_secret(pub, prv2, sec2) == 1); + ASSERT(memcmp(sec1, sec2, sizeof(sec1)) == 0); + pub2[63] ^= 1; // Not on the curve anymore + ASSERT(mg_p256_shared_secret(pub2, x, sec1) == 0); #endif } @@ -4658,8 +4661,8 @@ static void test_jwt(void) { opts.kid = mg_str("ec-key-1"); opts.private_key = key; opts.public_key = pub; - ASSERT(mg_uecc_parse_private_key(mg_str(key_pem), key, sizeof(key)) == 32); - ASSERT(mg_uecc_compute_public_key(key, pub, mg_uecc_secp256r1()) == 1); + ASSERT(mg_p256_parse_private_key(mg_str(key_pem), key, sizeof(key)) == 32); + ASSERT(mg_p256_compute_public_key(key, pub) == 1); n = mg_jwt_sign_es256(&opts, jwt, sizeof(jwt)); ASSERT(n > 0 && n + 1 < sizeof(jwt)); jwt[n] = '\0'; @@ -4681,7 +4684,7 @@ static void test_crypto(void) { test_sha1(); test_sha256(); test_sha384(); - test_x25519(); + test_p256(); test_rsa(); test_jwt(); } diff --git a/tutorials/http/jwt-auth/main.c b/tutorials/http/jwt-auth/main.c index 0a253f52..2e0a94ad 100644 --- a/tutorials/http/jwt-auth/main.c +++ b/tutorials/http/jwt-auth/main.c @@ -21,9 +21,9 @@ static size_t verify_es256(struct mg_str token, char *claims, size_t len) { struct mg_jwt_opts opts = {.public_key = pub}; // This keeps the example self-contained. A real verifier would usually keep // only the issuer public key, selected by kid, not the signing private key. - if (mg_uecc_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key)) != + if (mg_p256_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key)) != 32 || - mg_uecc_compute_public_key(key, pub, mg_uecc_secp256r1()) != 1) { + mg_p256_compute_public_key(key, pub) != 1) { return 0; } return mg_jwt_verify_es256(token, &opts, claims, len); @@ -65,10 +65,9 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) { .private_key = key}; size_t keysz, n; keysz = - mg_uecc_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key)); + mg_p256_parse_private_key(mg_str(s_ec_private_key), key, sizeof(key)); // ES256 uses a raw 64-byte r || s signature. mg_jwt_sign_es256() signs it - // deterministically; applications can also hash the signing input and - // call mg_uecc_sign() directly if they need random-nonce ECDSA. + // deterministically (RFC 6979), see mg_p256_sign() mg_snprintf(claims, CLAIMS_LEN, "{%m:%m,%m:%m}", MG_ESC("sub"), MG_ESC("admin"), MG_ESC("scope"), MG_ESC("dash:read")); opts.claims = mg_str(claims); diff --git a/tutorials/nxp/frdm-imxrt1186/minimal/main.c b/tutorials/nxp/frdm-imxrt1186/minimal/main.c index b1cd108d..43e8db2e 100644 --- a/tutorials/nxp/frdm-imxrt1186/minimal/main.c +++ b/tutorials/nxp/frdm-imxrt1186/minimal/main.c @@ -37,8 +37,35 @@ bool mg_random(void *buf, size_t len) { } #endif +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -70,6 +97,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/nxp/frdm-imxrt1186/minimal/mongoose_config.h b/tutorials/nxp/frdm-imxrt1186/minimal/mongoose_config.h index 1d5770ed..eaafbd1b 100644 --- a/tutorials/nxp/frdm-imxrt1186/minimal/mongoose_config.h +++ b/tutorials/nxp/frdm-imxrt1186/minimal/mongoose_config.h @@ -1,4 +1,5 @@ #define MG_ARCH MG_ARCH_ARMGCC +#define MG_TLS MG_TLS_BUILTIN #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_NETC 1 #define MG_OTA MG_OTA_RT1180 diff --git a/tutorials/nxp/frdm-mcxe247/minimal/main.c b/tutorials/nxp/frdm-mcxe247/minimal/main.c index 648e1ff1..5ee99234 100644 --- a/tutorials/nxp/frdm-mcxe247/minimal/main.c +++ b/tutorials/nxp/frdm-mcxe247/minimal/main.c @@ -61,8 +61,35 @@ bool mg_random(void *buf, size_t len) { } #endif +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -97,6 +124,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/nxp/frdm-mcxe247/minimal/mongoose_config.h b/tutorials/nxp/frdm-mcxe247/minimal/mongoose_config.h index 67ebc1c1..c4e40b58 100644 --- a/tutorials/nxp/frdm-mcxe247/minimal/mongoose_config.h +++ b/tutorials/nxp/frdm-mcxe247/minimal/mongoose_config.h @@ -2,6 +2,7 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_ARMGCC +#define MG_TLS MG_TLS_BUILTIN #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_MCXE 1 diff --git a/tutorials/nxp/frdm-mcxn947/minimal/main.c b/tutorials/nxp/frdm-mcxn947/minimal/main.c index 8c137484..3124584c 100644 --- a/tutorials/nxp/frdm-mcxn947/minimal/main.c +++ b/tutorials/nxp/frdm-mcxn947/minimal/main.c @@ -69,8 +69,35 @@ bool mg_random(void *buf, size_t len) { } #endif +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -105,6 +132,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/nxp/frdm-mcxn947/minimal/mongoose_config.h b/tutorials/nxp/frdm-mcxn947/minimal/mongoose_config.h index 3f272122..98c7be19 100644 --- a/tutorials/nxp/frdm-mcxn947/minimal/mongoose_config.h +++ b/tutorials/nxp/frdm-mcxn947/minimal/mongoose_config.h @@ -2,6 +2,7 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_ARMGCC +#define MG_TLS MG_TLS_BUILTIN #define MG_OTA MG_OTA_MCXN #define MG_ENABLE_TCPIP 1 diff --git a/tutorials/nxp/frdm-rw612/minimal/main.c b/tutorials/nxp/frdm-rw612/minimal/main.c index 789db3bb..9a87e535 100644 --- a/tutorials/nxp/frdm-rw612/minimal/main.c +++ b/tutorials/nxp/frdm-rw612/minimal/main.c @@ -37,8 +37,35 @@ bool mg_random(void *buf, size_t len) { } #endif +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/nxp/rt1060-evkb/minimal/main.c b/tutorials/nxp/rt1060-evkb/minimal/main.c index 18dbb052..b4344d65 100644 --- a/tutorials/nxp/rt1060-evkb/minimal/main.c +++ b/tutorials/nxp/rt1060-evkb/minimal/main.c @@ -33,8 +33,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -64,6 +91,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/nxp/rt1060-evkb/minimal/mongoose_config.h b/tutorials/nxp/rt1060-evkb/minimal/mongoose_config.h index e7984d1e..9885a54f 100644 --- a/tutorials/nxp/rt1060-evkb/minimal/mongoose_config.h +++ b/tutorials/nxp/rt1060-evkb/minimal/mongoose_config.h @@ -2,6 +2,7 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_ARMGCC +#define MG_TLS MG_TLS_BUILTIN #define MG_OTA MG_OTA_RT1060 #define MG_ENABLE_TCPIP 1 diff --git a/tutorials/nxp/rt1170-evkb/minimal/main.c b/tutorials/nxp/rt1170-evkb/minimal/main.c index 3815eb76..5f38f234 100644 --- a/tutorials/nxp/rt1170-evkb/minimal/main.c +++ b/tutorials/nxp/rt1170-evkb/minimal/main.c @@ -37,8 +37,35 @@ bool mg_random(void *buf, size_t len) { } #endif +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -70,6 +97,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/nxp/rt1170-evkb/minimal/mongoose_config.h b/tutorials/nxp/rt1170-evkb/minimal/mongoose_config.h index 32891715..44d80969 100644 --- a/tutorials/nxp/rt1170-evkb/minimal/mongoose_config.h +++ b/tutorials/nxp/rt1170-evkb/minimal/mongoose_config.h @@ -2,6 +2,7 @@ // See https://mongoose.ws/documentation/#build-options #define MG_ARCH MG_ARCH_ARMGCC +#define MG_TLS MG_TLS_BUILTIN #define MG_ENABLE_TCPIP 1 #define MG_ENABLE_DRIVER_IMXRT11 1 diff --git a/tutorials/rp/pico-rm2/CMakeLists.txt b/tutorials/rp/pico-rm2/CMakeLists.txt index 8d469c7e..0b5a00a2 100644 --- a/tutorials/rp/pico-rm2/CMakeLists.txt +++ b/tutorials/rp/pico-rm2/CMakeLists.txt @@ -7,8 +7,8 @@ include(pico_sdk_import.cmake) project(firmware) pico_sdk_init() -add_executable(firmware main.c wifi.c mongoose/mongoose.c) -target_include_directories(firmware PRIVATE mongoose) +add_executable(firmware main.c wifi.c ../../../mongoose.c) +target_include_directories(firmware PRIVATE ../../.. ./) target_link_libraries(firmware hardware_pio pico_stdlib pico_rand hardware_flash pico_bootrom) pico_add_extra_outputs(firmware) # create map/bin/hex file etc. diff --git a/tutorials/rp/pico-rm2/Makefile b/tutorials/rp/pico-rm2/Makefile index 009adbb1..79e32298 100644 --- a/tutorials/rp/pico-rm2/Makefile +++ b/tutorials/rp/pico-rm2/Makefile @@ -14,13 +14,11 @@ endif all build example: build/firmware.uf2 -build/firmware.uf2: pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h +build/firmware.uf2: pico_sdk_import.cmake pico_sdk_import.cmake: $(SDK_PATH) cp $(SDK_PATH)/external/$@ $@ -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../$(notdir $@) $@ build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) @@ -30,7 +28,7 @@ flash: build/firmware.uf2 picotool load $< -f clean: - $(RM) build pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h $(SDK_PATH) + $(RM) build pico_sdk_import.cmake $(SDK_PATH) $(SDK_PATH): git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ diff --git a/tutorials/rp/pico-rm2/mongoose/mongoose_config.h b/tutorials/rp/pico-rm2/mongoose_config.h similarity index 100% rename from tutorials/rp/pico-rm2/mongoose/mongoose_config.h rename to tutorials/rp/pico-rm2/mongoose_config.h diff --git a/tutorials/rp/pico-rmii/CMakeLists.txt b/tutorials/rp/pico-rmii/CMakeLists.txt index d85f6a4b..4b400d72 100644 --- a/tutorials/rp/pico-rmii/CMakeLists.txt +++ b/tutorials/rp/pico-rmii/CMakeLists.txt @@ -7,8 +7,8 @@ include(pico_sdk_import.cmake) project(firmware) pico_sdk_init() -add_executable(firmware main.c driver_rp2040_rmii.c mongoose/mongoose.c) -target_include_directories(firmware PRIVATE . mongoose) +add_executable(firmware main.c driver_rp2040_rmii.c ../../../mongoose.c) +target_include_directories(firmware PRIVATE ../../.. ./) target_link_libraries(firmware hardware_pio hardware_dma pico_stdlib pico_rand) pico_add_extra_outputs(firmware) # create map/bin/hex file etc. diff --git a/tutorials/rp/pico-rmii/Makefile b/tutorials/rp/pico-rmii/Makefile index 009adbb1..79e32298 100644 --- a/tutorials/rp/pico-rmii/Makefile +++ b/tutorials/rp/pico-rmii/Makefile @@ -14,13 +14,11 @@ endif all build example: build/firmware.uf2 -build/firmware.uf2: pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h +build/firmware.uf2: pico_sdk_import.cmake pico_sdk_import.cmake: $(SDK_PATH) cp $(SDK_PATH)/external/$@ $@ -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../$(notdir $@) $@ build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) @@ -30,7 +28,7 @@ flash: build/firmware.uf2 picotool load $< -f clean: - $(RM) build pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h $(SDK_PATH) + $(RM) build pico_sdk_import.cmake $(SDK_PATH) $(SDK_PATH): git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ diff --git a/tutorials/rp/pico-rmii/mongoose/mongoose_config.h b/tutorials/rp/pico-rmii/mongoose_config.h similarity index 100% rename from tutorials/rp/pico-rmii/mongoose/mongoose_config.h rename to tutorials/rp/pico-rmii/mongoose_config.h diff --git a/tutorials/rp/pico-rndis-device/CMakeLists.txt b/tutorials/rp/pico-rndis-device/CMakeLists.txt index 2c8ca9a0..59a32d9a 100644 --- a/tutorials/rp/pico-rndis-device/CMakeLists.txt +++ b/tutorials/rp/pico-rndis-device/CMakeLists.txt @@ -7,12 +7,13 @@ pico_sdk_init() add_executable(firmware main.c usb_descriptors.c - mongoose.c + ../../../mongoose.c net.c pico-sdk/lib/tinyusb/lib/networking/rndis_reports.c) target_include_directories(firmware PUBLIC - . + ../../.. + ./ pico-sdk/lib/tinyusb/lib/networking) target_link_libraries(firmware pico_stdlib pico_rand hardware_spi tinyusb_device) diff --git a/tutorials/rp/pico-rndis-device/mongoose.c b/tutorials/rp/pico-rndis-device/mongoose.c deleted file mode 120000 index 5e522bbc..00000000 --- a/tutorials/rp/pico-rndis-device/mongoose.c +++ /dev/null @@ -1 +0,0 @@ -../../../mongoose.c \ No newline at end of file diff --git a/tutorials/rp/pico-rndis-device/mongoose.h b/tutorials/rp/pico-rndis-device/mongoose.h deleted file mode 120000 index ee4ac823..00000000 --- a/tutorials/rp/pico-rndis-device/mongoose.h +++ /dev/null @@ -1 +0,0 @@ -../../../mongoose.h \ No newline at end of file diff --git a/tutorials/rp/pico-rndis/CMakeLists.txt b/tutorials/rp/pico-rndis/CMakeLists.txt index 3f020afa..d92f1e3a 100644 --- a/tutorials/rp/pico-rndis/CMakeLists.txt +++ b/tutorials/rp/pico-rndis/CMakeLists.txt @@ -11,12 +11,12 @@ add_executable(firmware main.c msc_disk.c usb_descriptors.c - mongoose/mongoose.c + ../../../mongoose.c pico-sdk/lib/tinyusb/lib/networking/rndis_reports.c) target_include_directories(firmware PRIVATE - . - mongoose + ../../.. + ./ pico-sdk/lib/tinyusb/lib/networking) target_link_libraries(firmware pico_stdlib pico_rand hardware_spi tinyusb_device) diff --git a/tutorials/rp/pico-rndis/Makefile b/tutorials/rp/pico-rndis/Makefile index 009adbb1..79e32298 100644 --- a/tutorials/rp/pico-rndis/Makefile +++ b/tutorials/rp/pico-rndis/Makefile @@ -14,13 +14,11 @@ endif all build example: build/firmware.uf2 -build/firmware.uf2: pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h +build/firmware.uf2: pico_sdk_import.cmake pico_sdk_import.cmake: $(SDK_PATH) cp $(SDK_PATH)/external/$@ $@ -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../$(notdir $@) $@ build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) @@ -30,7 +28,7 @@ flash: build/firmware.uf2 picotool load $< -f clean: - $(RM) build pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h $(SDK_PATH) + $(RM) build pico_sdk_import.cmake $(SDK_PATH) $(SDK_PATH): git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ diff --git a/tutorials/rp/pico-rndis/mongoose/mongoose_config.h b/tutorials/rp/pico-rndis/mongoose_config.h similarity index 100% rename from tutorials/rp/pico-rndis/mongoose/mongoose_config.h rename to tutorials/rp/pico-rndis/mongoose_config.h diff --git a/tutorials/rp/pico-w/CMakeLists.txt b/tutorials/rp/pico-w/CMakeLists.txt index aca447eb..1fa36b76 100644 --- a/tutorials/rp/pico-w/CMakeLists.txt +++ b/tutorials/rp/pico-w/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.13) # If you will set board type here, do it BEFORE including the SDK -set(PICO_BOARD pico_w) +set(PICO_BOARD pico2_w) include(pico_sdk_import.cmake) project(firmware) pico_sdk_init() -add_executable(firmware main.c wifi.c mongoose/mongoose.c) -target_include_directories(firmware PRIVATE mongoose) +add_executable(firmware main.c wifi.c ../../../mongoose.c) +target_include_directories(firmware PRIVATE ../../.. ./) target_link_libraries(firmware pico_stdlib hardware_spi pico_rand pico_cyw43_driver pico_cyw43_arch_none hardware_flash pico_bootrom) pico_add_extra_outputs(firmware) # create map/bin/hex file etc. diff --git a/tutorials/rp/pico-w/Makefile b/tutorials/rp/pico-w/Makefile index 009adbb1..79e32298 100644 --- a/tutorials/rp/pico-w/Makefile +++ b/tutorials/rp/pico-w/Makefile @@ -14,13 +14,11 @@ endif all build example: build/firmware.uf2 -build/firmware.uf2: pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h +build/firmware.uf2: pico_sdk_import.cmake pico_sdk_import.cmake: $(SDK_PATH) cp $(SDK_PATH)/external/$@ $@ -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../$(notdir $@) $@ build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) @@ -30,7 +28,7 @@ flash: build/firmware.uf2 picotool load $< -f clean: - $(RM) build pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h $(SDK_PATH) + $(RM) build pico_sdk_import.cmake $(SDK_PATH) $(SDK_PATH): git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ diff --git a/tutorials/rp/pico-w/main.c b/tutorials/rp/pico-w/main.c index 9941eb02..ae9919ea 100644 --- a/tutorials/rp/pico-w/main.c +++ b/tutorials/rp/pico-w/main.c @@ -11,8 +11,35 @@ static void blink_task(void) { } } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; uint64_t tick = to_ms_since_boot(get_absolute_time()); if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { @@ -34,6 +61,7 @@ static void run_mongoose(void) { mg_log_set_fn(log_fn, NULL); mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); for (;;) { mg_mgr_poll(&mgr, 1); diff --git a/tutorials/rp/pico-w/mongoose/mongoose_config.h b/tutorials/rp/pico-w/mongoose_config.h similarity index 93% rename from tutorials/rp/pico-w/mongoose/mongoose_config.h rename to tutorials/rp/pico-w/mongoose_config.h index 1cb1895f..ac4d4851 100644 --- a/tutorials/rp/pico-w/mongoose/mongoose_config.h +++ b/tutorials/rp/pico-w/mongoose_config.h @@ -2,6 +2,7 @@ // Build options: https://mongoose.ws/docs/getting-started/build-options/ #define MG_ARCH MG_ARCH_PICOSDK +#define MG_TLS MG_TLS_BUILTIN #define MG_OTA MG_OTA_PICOSDK #define MG_IO_SIZE 2048 diff --git a/tutorials/rp/w5500-evb-pico/CMakeLists.txt b/tutorials/rp/w5500-evb-pico/CMakeLists.txt index 483354a5..e55ee9bd 100644 --- a/tutorials/rp/w5500-evb-pico/CMakeLists.txt +++ b/tutorials/rp/w5500-evb-pico/CMakeLists.txt @@ -7,8 +7,8 @@ include(pico_sdk_import.cmake) project(firmware) pico_sdk_init() -add_executable(firmware main.c mongoose/mongoose.c) -target_include_directories(firmware PRIVATE mongoose) +add_executable(firmware main.c ../../../mongoose.c) +target_include_directories(firmware PRIVATE ../../.. ./) target_link_libraries(firmware pico_stdlib hardware_spi pico_rand hardware_flash pico_bootrom) pico_add_extra_outputs(firmware) # create map/bin/hex file etc. diff --git a/tutorials/rp/w5500-evb-pico/Makefile b/tutorials/rp/w5500-evb-pico/Makefile index 8f0d2814..42b9ea52 100644 --- a/tutorials/rp/w5500-evb-pico/Makefile +++ b/tutorials/rp/w5500-evb-pico/Makefile @@ -14,13 +14,11 @@ endif all build example: build/firmware.uf2 -build/firmware.uf2: pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h +build/firmware.uf2: pico_sdk_import.cmake pico_sdk_import.cmake: $(SDK_PATH) cp $(SDK_PATH)/external/$@ $@ -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../$(notdir $@) $@ build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) @@ -30,7 +28,7 @@ flash: build/firmware.uf2 picotool load $< -f clean: - $(RM) build pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h $(SDK_PATH) + $(RM) build pico_sdk_import.cmake $(SDK_PATH) $(SDK_PATH): git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ diff --git a/tutorials/rp/w5500-evb-pico/main.c b/tutorials/rp/w5500-evb-pico/main.c index 5b0ea7e7..728caf0e 100644 --- a/tutorials/rp/w5500-evb-pico/main.c +++ b/tutorials/rp/w5500-evb-pico/main.c @@ -54,8 +54,35 @@ static void blink_task(void) { } } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; uint64_t tick = to_ms_since_boot(get_absolute_time()); if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { @@ -86,6 +113,7 @@ static void run_mongoose(void) { MG_INFO(("Driver: W5500, MAC: %M", mg_print_mac, mif.mac)); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); for (;;) { mg_mgr_poll(&mgr, 1); diff --git a/tutorials/rp/w5500-evb-pico/mongoose/mongoose_config.h b/tutorials/rp/w5500-evb-pico/mongoose_config.h similarity index 90% rename from tutorials/rp/w5500-evb-pico/mongoose/mongoose_config.h rename to tutorials/rp/w5500-evb-pico/mongoose_config.h index fda5ebf4..d134f6b9 100644 --- a/tutorials/rp/w5500-evb-pico/mongoose/mongoose_config.h +++ b/tutorials/rp/w5500-evb-pico/mongoose_config.h @@ -2,6 +2,7 @@ // Build options: https://mongoose.ws/docs/getting-started/build-options/ #define MG_ARCH MG_ARCH_PICOSDK +#define MG_TLS MG_TLS_BUILTIN #define MG_OTA MG_OTA_PICOSDK #define MG_IO_SIZE 2048 diff --git a/tutorials/rp/w55rp20-evb-pico/CMakeLists.txt b/tutorials/rp/w55rp20-evb-pico/CMakeLists.txt index 0882e294..487c3684 100644 --- a/tutorials/rp/w55rp20-evb-pico/CMakeLists.txt +++ b/tutorials/rp/w55rp20-evb-pico/CMakeLists.txt @@ -6,8 +6,8 @@ include(pico_sdk_import.cmake) project(firmware) pico_sdk_init() -add_executable(firmware main.c mongoose/mongoose.c) -target_include_directories(firmware PRIVATE mongoose) +add_executable(firmware main.c ../../../mongoose.c) +target_include_directories(firmware PRIVATE ../../.. ./) target_link_libraries(firmware pico_stdlib hardware_pio pico_rand hardware_flash pico_bootrom) pico_add_extra_outputs(firmware) # create map/bin/hex file etc. diff --git a/tutorials/rp/w55rp20-evb-pico/Makefile b/tutorials/rp/w55rp20-evb-pico/Makefile index 8f0d2814..42b9ea52 100644 --- a/tutorials/rp/w55rp20-evb-pico/Makefile +++ b/tutorials/rp/w55rp20-evb-pico/Makefile @@ -14,13 +14,11 @@ endif all build example: build/firmware.uf2 -build/firmware.uf2: pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h +build/firmware.uf2: pico_sdk_import.cmake pico_sdk_import.cmake: $(SDK_PATH) cp $(SDK_PATH)/external/$@ $@ -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../$(notdir $@) $@ build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) @@ -30,7 +28,7 @@ flash: build/firmware.uf2 picotool load $< -f clean: - $(RM) build pico_sdk_import.cmake mongoose/mongoose.c mongoose/mongoose.h $(SDK_PATH) + $(RM) build pico_sdk_import.cmake $(SDK_PATH) $(SDK_PATH): git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ diff --git a/tutorials/rp/w55rp20-evb-pico/main.c b/tutorials/rp/w55rp20-evb-pico/main.c index 8d32de55..84886208 100644 --- a/tutorials/rp/w55rp20-evb-pico/main.c +++ b/tutorials/rp/w55rp20-evb-pico/main.c @@ -167,8 +167,35 @@ static void blink_task(void) { } } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; uint64_t tick = to_ms_since_boot(get_absolute_time()); if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { @@ -191,6 +218,7 @@ static void run_mongoose(void) { mg_log_set_fn(log_fn, NULL); mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); for (;;) { mg_mgr_poll(&mgr, 1); diff --git a/tutorials/rp/w55rp20-evb-pico/mongoose/mongoose_config.h b/tutorials/rp/w55rp20-evb-pico/mongoose_config.h similarity index 100% rename from tutorials/rp/w55rp20-evb-pico/mongoose/mongoose_config.h rename to tutorials/rp/w55rp20-evb-pico/mongoose_config.h diff --git a/tutorials/stm32/nucleo-f429zi/minimal/main.c b/tutorials/stm32/nucleo-f429zi/minimal/main.c index 69058451..34d521fb 100644 --- a/tutorials/stm32/nucleo-f429zi/minimal/main.c +++ b/tutorials/stm32/nucleo-f429zi/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-f746zg/minimal/main.c b/tutorials/stm32/nucleo-f746zg/minimal/main.c index f186d5a2..a9af858a 100644 --- a/tutorials/stm32/nucleo-f746zg/minimal/main.c +++ b/tutorials/stm32/nucleo-f746zg/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-f756zg/minimal/main.c b/tutorials/stm32/nucleo-f756zg/minimal/main.c index f186d5a2..a9af858a 100644 --- a/tutorials/stm32/nucleo-f756zg/minimal/main.c +++ b/tutorials/stm32/nucleo-f756zg/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-h563zi/minimal/link.ld b/tutorials/stm32/nucleo-h563zi/minimal/link.ld index fac73050..b84f2768 100644 --- a/tutorials/stm32/nucleo-h563zi/minimal/link.ld +++ b/tutorials/stm32/nucleo-h563zi/minimal/link.ld @@ -4,7 +4,7 @@ MEMORY { sram(rwx) : ORIGIN = 0x20000000, LENGTH = 640k } _estack = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */ -_sstack = _estack - 4098; +_sstack = _estack - 16384; /* Stack limit, MSPLIM. TLS needs > 4 KB */ SECTIONS { .vectors : { KEEP(*(.isr_vector)) } > flash diff --git a/tutorials/stm32/nucleo-h563zi/minimal/main.c b/tutorials/stm32/nucleo-h563zi/minimal/main.c index 864a8c3d..9afc5886 100644 --- a/tutorials/stm32/nucleo-h563zi/minimal/main.c +++ b/tutorials/stm32/nucleo-h563zi/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -80,6 +107,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-h563zi/minimal/mongoose_config.h b/tutorials/stm32/nucleo-h563zi/minimal/mongoose_config.h index d1a9dff6..d8a72d25 100644 --- a/tutorials/stm32/nucleo-h563zi/minimal/mongoose_config.h +++ b/tutorials/stm32/nucleo-h563zi/minimal/mongoose_config.h @@ -38,3 +38,5 @@ mac[4] = MGUID[1] & 255; \ mac[5] = MGUID[2] & 255; \ } while (0) + +#define MG_ENABLE_HEALTH 1 diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/Makefile b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/Makefile index 2dc5992f..c5c5b1c5 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/Makefile +++ b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/Makefile @@ -1,23 +1,21 @@ # Copyright (c) 2026 Cesanta Software Limited # Environment setup: https://mongoose.ws/docs/getting-started/build-environment/ +MGDIR = ../../../.. CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include -Imongoose +CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA) LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map SOURCES = main.c hal.c echo.c SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s -SOURCES += mongoose/mongoose.c +SOURCES += $(MGDIR)/mongoose.c all build example: firmware.bin -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../../mongoose.[ch] mongoose/ - -firmware.elf: cmsis_core cmsis_h7 hal.h link.ld Makefile $(SOURCES) mongoose/mongoose.h +firmware.elf: cmsis_core cmsis_h7 hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ firmware.bin: firmware.elf @@ -35,4 +33,4 @@ cmsis_h7: git clone -q -c advice.detachedHead=false --depth 1 -b v1.10.6 https://github.com/STMicroelectronics/cmsis_device_h7 $@ clean: - rm -rf firmware.* cmsis_* mongoose/mongoose.* + rm -rf firmware.* cmsis_* diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/echo.c b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/echo.c index a2f095df..8a805f92 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/echo.c +++ b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/echo.c @@ -114,3 +114,21 @@ void echo_poll(void) { p = &c->next; } } + +// On desktop, build with -DMAIN flag to include main(). +// On embedded system, run this code in your main() function +#ifdef MAIN +int main(void) { + struct mg_mgr mgr; + + mg_mgr_init(&mgr); + echo_init(1234); + + for (;;) { + mg_mgr_poll(&mgr, 1); + echo_poll(); + } + + return 0; +} +#endif diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose.c b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose.c deleted file mode 120000 index 7a2752cb..00000000 --- a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose.c +++ /dev/null @@ -1 +0,0 @@ -../../../../mongoose.c \ No newline at end of file diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose.h b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose.h deleted file mode 120000 index daff1633..00000000 --- a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose.h +++ /dev/null @@ -1 +0,0 @@ -../../../../mongoose.h \ No newline at end of file diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose_config.h b/tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose_config.h similarity index 100% rename from tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose/mongoose_config.h rename to tutorials/stm32/nucleo-h723zg/minimal-bsd-baremetal/mongoose_config.h diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/Makefile b/tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/Makefile index b13ba2e9..1243f492 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/Makefile +++ b/tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/Makefile @@ -1,16 +1,17 @@ # Copyright (c) 2026 Cesanta Software Limited # Environment setup: https://mongoose.ws/docs/getting-started/build-environment/ +MGDIR = ../../../.. CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion CFLAGS += -g3 -Os -ffunction-sections -fdata-sections -CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include -Imongoose +CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA) LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections SOURCES = main.c hal.c SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s -SOURCES += mongoose/mongoose.c +SOURCES += $(MGDIR)/mongoose.c # FreeRTOS. H7 has a Cortex-M7 r1p0 core, FreeRTOS recommends using CM4F port for non-r0p1 CM7 micros SOURCES += FreeRTOS-Kernel/tasks.c FreeRTOS-Kernel/list.c FreeRTOS-Kernel/queue.c @@ -21,10 +22,7 @@ CFLAGS += -IFreeRTOS-Kernel/portable/GCC/ARM_CM4F -Wno-conversion all build example: firmware.bin -mongoose/mongoose.c mongoose/mongoose.h: - cp ../../../../mongoose.[ch] mongoose/ - -firmware.elf: cmsis_core cmsis_h7 FreeRTOS-Kernel hal.h link.ld Makefile $(SOURCES) mongoose/mongoose.h +firmware.elf: cmsis_core cmsis_h7 FreeRTOS-Kernel hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@ firmware.bin: firmware.elf @@ -49,7 +47,7 @@ FreeRTOS-Kernel: git clone --depth 1 -b V11.3.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@ clean: - rm -rf firmware.* cmsis_* mongoose/mongoose.* FreeRTOS-Kernel + rm -rf firmware.* cmsis_* FreeRTOS-Kernel # Automated remote test. Requires env variable VCON_API_KEY set. See https://vcon.io/automated-firmware-tests/ diff --git a/tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/mongoose/mongoose_config.h b/tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/mongoose_config.h similarity index 100% rename from tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/mongoose/mongoose_config.h rename to tutorials/stm32/nucleo-h723zg/minimal-bsd-freertos/mongoose_config.h diff --git a/tutorials/stm32/nucleo-h723zg/minimal/main.c b/tutorials/stm32/nucleo-h723zg/minimal/main.c index 34d597cd..d6a954f3 100644 --- a/tutorials/stm32/nucleo-h723zg/minimal/main.c +++ b/tutorials/stm32/nucleo-h723zg/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-h743zi/minimal/main.c b/tutorials/stm32/nucleo-h743zi/minimal/main.c index 396eb942..bffe9198 100644 --- a/tutorials/stm32/nucleo-h743zi/minimal/main.c +++ b/tutorials/stm32/nucleo-h743zi/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-n657x0-q/minimal/link.ld b/tutorials/stm32/nucleo-n657x0-q/minimal/link.ld index b8976094..24d2b600 100644 --- a/tutorials/stm32/nucleo-n657x0-q/minimal/link.ld +++ b/tutorials/stm32/nucleo-n657x0-q/minimal/link.ld @@ -4,7 +4,7 @@ MEMORY { sram (rwx) : ORIGIN = 0x34000000, LENGTH = 2048K } _estack = ORIGIN(sram) + LENGTH(sram); -_sstack = _estack - 4096; +_sstack = _estack - 16384; /* Stack limit, MSPLIM. TLS needs > 4 KB */ SECTIONS { .vectors : { KEEP(*(.isr_vector)) } > flash diff --git a/tutorials/stm32/nucleo-n657x0-q/minimal/main.c b/tutorials/stm32/nucleo-n657x0-q/minimal/main.c index e5a9678b..24ea16ac 100644 --- a/tutorials/stm32/nucleo-n657x0-q/minimal/main.c +++ b/tutorials/stm32/nucleo-n657x0-q/minimal/main.c @@ -36,8 +36,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/nucleo-u5a5zj-q/minimal/main.c b/tutorials/stm32/nucleo-u5a5zj-q/minimal/main.c index a3b774a1..3bca52ab 100644 --- a/tutorials/stm32/nucleo-u5a5zj-q/minimal/main.c +++ b/tutorials/stm32/nucleo-u5a5zj-q/minimal/main.c @@ -32,8 +32,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -66,6 +93,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/portenta-h7/minimal/main.c b/tutorials/stm32/portenta-h7/minimal/main.c index 635ffb1f..62720f30 100644 --- a/tutorials/stm32/portenta-h7/minimal/main.c +++ b/tutorials/stm32/portenta-h7/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -74,6 +101,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/stm32h573i-dk/minimal/main.c b/tutorials/stm32/stm32h573i-dk/minimal/main.c index 57374d3d..ea1fde63 100644 --- a/tutorials/stm32/stm32h573i-dk/minimal/main.c +++ b/tutorials/stm32/stm32h573i-dk/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free())); diff --git a/tutorials/stm32/stm32h747i-disco/minimal/main.c b/tutorials/stm32/stm32h747i-disco/minimal/main.c index afb77094..ecaeb215 100644 --- a/tutorials/stm32/stm32h747i-disco/minimal/main.c +++ b/tutorials/stm32/stm32h747i-disco/minimal/main.c @@ -40,8 +40,35 @@ bool mg_random(void *buf, size_t len) { return true; } +// Self-signed P-256 certificate and key for the HTTPS listener. Created by: +// openssl ecparam -genkey -name prime256v1 -noout -out key.pem +// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg +#define TLS_CERT \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \ + "FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \ + "MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \ + "SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \ + "uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \ + "MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \ + "BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \ + "lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \ + "BCjX++LI\n" \ + "-----END CERTIFICATE-----\n" + +#define TLS_KEY \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \ + "AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \ + "LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \ + "-----END EC PRIVATE KEY-----\n" + static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener + struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT), + .key = mg_str(TLS_KEY)}; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick()); @@ -73,6 +100,7 @@ int main(void) { struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL); MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000)); MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));