From 7589e53fea04d2bada44b7729432a3ee2a9237a4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 29 Jun 2022 16:31:55 -0700 Subject: [PATCH] WIP websocket accept and hashlib --- locale/circuitpython.pot | 4 + ports/espressif/common-hal/hashlib/Hash.c | 52 +++++++++ ports/espressif/common-hal/hashlib/Hash.h | 41 +++++++ ports/espressif/common-hal/hashlib/__init__.c | 40 +++++++ ports/espressif/mpconfigport.mk | 1 + py/circuitpy_defns.mk | 5 + shared-bindings/hashlib/Hash.c | 103 ++++++++++++++++++ shared-bindings/hashlib/Hash.h | 43 ++++++++ shared-bindings/hashlib/__init__.c | 90 +++++++++++++++ shared-bindings/hashlib/__init__.h | 36 ++++++ .../shared/web_workflow/static/serial.html | 16 +++ .../shared/web_workflow/static/serial.js | 43 ++++++++ .../shared/web_workflow/static/welcome.html | 2 +- supervisor/shared/web_workflow/web_workflow.c | 40 +++++++ 14 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 ports/espressif/common-hal/hashlib/Hash.c create mode 100644 ports/espressif/common-hal/hashlib/Hash.h create mode 100644 ports/espressif/common-hal/hashlib/__init__.c create mode 100644 shared-bindings/hashlib/Hash.c create mode 100644 shared-bindings/hashlib/Hash.h create mode 100644 shared-bindings/hashlib/__init__.c create mode 100644 shared-bindings/hashlib/__init__.h create mode 100644 supervisor/shared/web_workflow/static/serial.html create mode 100644 supervisor/shared/web_workflow/static/serial.js diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 5740e1e288..a66a386f1d 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -2199,6 +2199,10 @@ msgstr "" msgid "Unsupported format" msgstr "" +#: shared-bindings/hashlib/__init__.c +msgid "Unsupported hash algorithm" +msgstr "" + #: ports/espressif/common-hal/dualbank/__init__.c msgid "Update Failed" msgstr "" diff --git a/ports/espressif/common-hal/hashlib/Hash.c b/ports/espressif/common-hal/hashlib/Hash.c new file mode 100644 index 0000000000..8e2ab02edb --- /dev/null +++ b/ports/espressif/common-hal/hashlib/Hash.c @@ -0,0 +1,52 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#include "shared-bindings/hashlib/Hash.h" + +#include "components/mbedtls/mbedtls/include/mbedtls/ssl.h" + +void common_hal_hashlib_hash_update(hashlib_hash_obj_t *self, const uint8_t *data, size_t datalen) { + if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { + mbedtls_sha1_update_ret(&self->sha1, data, datalen); + return; + } +} + +void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, size_t datalen) { + if (datalen < common_hal_hashlib_hash_get_digest_size(self)) { + return; + } + if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { + mbedtls_sha1_finish_ret(&self->sha1, data); + } +} + +size_t common_hal_hashlib_hash_get_digest_size(hashlib_hash_obj_t *self) { + if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { + return 20; + } + return 0; +} diff --git a/ports/espressif/common-hal/hashlib/Hash.h b/ports/espressif/common-hal/hashlib/Hash.h new file mode 100644 index 0000000000..ece282833e --- /dev/null +++ b/ports/espressif/common-hal/hashlib/Hash.h @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_HASHLIB_HASH_H +#define MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_HASHLIB_HASH_H + +#include "components/mbedtls/mbedtls/include/mbedtls/sha1.h" + +typedef struct { + mp_obj_base_t base; + union { + mbedtls_sha1_context sha1; + }; + // Of MBEDTLS_SSL_HASH_* + uint8_t hash_type; +} hashlib_hash_obj_t; + +#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_HASHLIB_HASH_H diff --git a/ports/espressif/common-hal/hashlib/__init__.c b/ports/espressif/common-hal/hashlib/__init__.c new file mode 100644 index 0000000000..1e6b2a4802 --- /dev/null +++ b/ports/espressif/common-hal/hashlib/__init__.c @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#include "shared-bindings/hashlib/__init__.h" + +#include "components/mbedtls/mbedtls/include/mbedtls/ssl.h" + + +bool common_hal_hashlib_new(hashlib_hash_obj_t *self, const char *algorithm) { + if (strcmp(algorithm, "sha1") == 0) { + self->hash_type = MBEDTLS_SSL_HASH_SHA1; + mbedtls_sha1_init(&self->sha1); + mbedtls_sha1_starts_ret(&self->sha1); + return true; + } + return false; +} diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 2eeb86e49d..ddaa4836d3 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -19,6 +19,7 @@ CIRCUITPY_COUNTIO ?= 1 CIRCUITPY_DUALBANK ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_FREQUENCYIO ?= 1 +CIRCUITPY_HASHLIB ?= 1 CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_I2CPERIPHERAL ?= 1 CIRCUITPY_RGBMATRIX ?= 1 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index c5c89ab19f..af49c876ba 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -207,6 +207,9 @@ endif ifeq ($(CIRCUITPY_GNSS),1) SRC_PATTERNS += gnss/% endif +ifeq ($(CIRCUITPY_HASHLIB),1) +SRC_PATTERNS += hashlib/% +endif ifeq ($(CIRCUITPY_I2CPERIPHERAL),1) SRC_PATTERNS += i2cperipheral/% endif @@ -419,6 +422,8 @@ SRC_COMMON_HAL_ALL = \ gnss/GNSS.c \ gnss/PositionFix.c \ gnss/SatelliteSystem.c \ + hashlib/__init__.c \ + hashlib/Hash.c \ i2cperipheral/I2CPeripheral.c \ i2cperipheral/__init__.c \ microcontroller/Pin.c \ diff --git a/shared-bindings/hashlib/Hash.c b/shared-bindings/hashlib/Hash.c new file mode 100644 index 0000000000..5dab05fa10 --- /dev/null +++ b/shared-bindings/hashlib/Hash.c @@ -0,0 +1,103 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#include "shared-bindings/hashlib/Hash.h" + +// #include "shared-bindings/util.h" + +// #include "shared/runtime/buffer_helper.h" +// #include "shared/runtime/interrupt_char.h" + +// #include "py/mperrno.h" +// #include "py/mphal.h" +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/objstr.h" +#include "py/runtime.h" + +//| class Hash: +//| """In progress hash algorithm. This object is always created by a `hashlib.new()`. It has no +//| user-visible constructor.""" +//| + +//| digest_size: int +//| """Digest size in bytes""" +//| +STATIC mp_obj_t hashlib_hash_get_digest_size(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &hashlib_hash_type)); + hashlib_hash_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_hashlib_hash_get_digest_size(self)); +} +MP_PROPERTY_GETTER(hashlib_hash_digest_size_obj, hashlib_hash_get_digest_size); + +//| def update(self, data: ReadableBuffer) -> None: +//| """Update the hash with the given bytes. +//| +//| :param ~circuitpython_typing.ReadableBuffer data: Update the hash from data in this buffer""" +//| ... +//| +mp_obj_t hashlib_hash_update(mp_obj_t self_in, mp_obj_t buf_in) { + mp_check_self(mp_obj_is_type(self_in, &hashlib_hash_type)); + hashlib_hash_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + + common_hal_hashlib_hash_update(self, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(hashlib_hash_update_obj, hashlib_hash_update); + +//| def digest(self) -> bytes: +//| """Returns the current digest as bytes() with a length of `hashlib.Hash.digest_size`.""" +//| ... +//| +STATIC mp_obj_t hashlib_hash_digest(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &hashlib_hash_type)); + hashlib_hash_obj_t *self = MP_OBJ_TO_PTR(self_in); + + size_t size = common_hal_hashlib_hash_get_digest_size(self); + mp_obj_t obj = mp_obj_new_bytes_of_zeros(size); + mp_obj_str_t *o = MP_OBJ_TO_PTR(obj); + + common_hal_hashlib_hash_digest(self, (uint8_t *)o->data, size); + return obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(hashlib_hash_digest_obj, hashlib_hash_digest); + +STATIC const mp_rom_map_elem_t hashlib_hash_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_digest_size), MP_ROM_PTR(&hashlib_hash_digest_size_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&hashlib_hash_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&hashlib_hash_digest_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(hashlib_hash_locals_dict, hashlib_hash_locals_dict_table); + +const mp_obj_type_t hashlib_hash_type = { + { &mp_type_type }, + .name = MP_QSTR_Hash, + .locals_dict = (mp_obj_dict_t *)&hashlib_hash_locals_dict, +}; diff --git a/shared-bindings/hashlib/Hash.h b/shared-bindings/hashlib/Hash.h new file mode 100644 index 0000000000..f3845b02ff --- /dev/null +++ b/shared-bindings/hashlib/Hash.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_HASHLIB_HASH_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_HASHLIB_HASH_H + +#include "py/obj.h" + +#include "common-hal/hashlib/Hash.h" + +extern const mp_obj_type_t hashlib_hash_type; + +// So that new can call it when given data. +mp_obj_t hashlib_hash_update(mp_obj_t self_in, mp_obj_t buf_in); + +void common_hal_hashlib_hash_update(hashlib_hash_obj_t *self, const uint8_t *data, size_t datalen); +void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, size_t datalen); +size_t common_hal_hashlib_hash_get_digest_size(hashlib_hash_obj_t *self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_HASHLIB_HASH_H diff --git a/shared-bindings/hashlib/__init__.c b/shared-bindings/hashlib/__init__.c new file mode 100644 index 0000000000..4b5be0165b --- /dev/null +++ b/shared-bindings/hashlib/__init__.c @@ -0,0 +1,90 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#include + +#include "py/obj.h" +#include "py/mpconfig.h" +#include "py/runtime.h" +#include "shared-bindings/hashlib/__init__.h" +#include "shared-bindings/hashlib/Hash.h" +#include "supervisor/shared/translate/translate.h" + +//| """Hashing related functions +//| +//| |see_cpython_module| :mod:`cpython:hashlib`. +//| """ +//| +//| def new(name, data=b"") -> hashlib.Hash: +//| """Returns a Hash object setup for the named algorithm. Raises ValueError when the named +//| algorithm is unsupported. +//| +//| :return: a hash object for the given algorithm +//| :rtype: hashlib.Hash""" +//| ... +//| +STATIC mp_obj_t hashlib_new(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_name, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_name, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const char *algorithm = mp_obj_str_get_str(args[ARG_name].u_obj); + + hashlib_hash_obj_t *self = m_new_obj(hashlib_hash_obj_t); + self->base.type = &hashlib_hash_type; + + if (!common_hal_hashlib_new(self, algorithm)) { + mp_raise_ValueError(translate("Unsupported hash algorithm")); + } + + if (args[ARG_data].u_obj != mp_const_none) { + hashlib_hash_update(self, args[ARG_data].u_obj); + } + return self; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(hashlib_new_obj, 1, hashlib_new); + +STATIC const mp_rom_map_elem_t hashlib_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_hashlib) }, + + { MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&hashlib_new_obj) }, + + // Hash is deliberately omitted here because CPython doesn't expose the + // object on `hashlib` only the internal `_hashlib`. +}; + +STATIC MP_DEFINE_CONST_DICT(hashlib_module_globals, hashlib_module_globals_table); + +const mp_obj_module_t hashlib_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&hashlib_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_hashlib, hashlib_module, CIRCUITPY_HASHLIB); diff --git a/shared-bindings/hashlib/__init__.h b/shared-bindings/hashlib/__init__.h new file mode 100644 index 0000000000..ae47b546a4 --- /dev/null +++ b/shared-bindings/hashlib/__init__.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Scott Shawcroft for Adafruit Industries + * + * 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. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_HASHLIB___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_HASHLIB___INIT___H + +#include + +#include "shared-bindings/hashlib/Hash.h" + +bool common_hal_hashlib_new(hashlib_hash_obj_t *self, const char *algorithm); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_HASHLIB___INIT___H diff --git a/supervisor/shared/web_workflow/static/serial.html b/supervisor/shared/web_workflow/static/serial.html new file mode 100644 index 0000000000..766a7a23e2 --- /dev/null +++ b/supervisor/shared/web_workflow/static/serial.html @@ -0,0 +1,16 @@ + + + + Simple client + + + + +

+  
+ + + +
+ + diff --git a/supervisor/shared/web_workflow/static/serial.js b/supervisor/shared/web_workflow/static/serial.js new file mode 100644 index 0000000000..36c980dc08 --- /dev/null +++ b/supervisor/shared/web_workflow/static/serial.js @@ -0,0 +1,43 @@ + +var ws; + +function onSubmit() { + var input = document.getElementById("input"); + // You can send message to the Web Socket using ws.send. + ws.send(input.value); + output("send: " + input.value); + input.value = ""; + input.focus(); +} + +function onCloseClick() { + ws.close(); +} + +function output(str) { + var log = document.getElementById("log"); + log.innerHTML += str; +} + +// Connect to Web Socket +ws = new WebSocket("ws://cpy-f57ce8.local/cp/serial/"); +// ws = new WebSocket("ws://127.0.0.1:9001") + +// Set event handlers. +ws.onopen = function() { + output("onopen"); +}; + +ws.onmessage = function(e) { + // e.data contains received string. + output("onmessage: " + e.data); +}; + +ws.onclose = function() { + output("onclose"); +}; + +ws.onerror = function(e) { + output("onerror"); + console.log(e) +}; diff --git a/supervisor/shared/web_workflow/static/welcome.html b/supervisor/shared/web_workflow/static/welcome.html index 7fb68c302e..6ef1ccd767 100644 --- a/supervisor/shared/web_workflow/static/welcome.html +++ b/supervisor/shared/web_workflow/static/welcome.html @@ -3,8 +3,8 @@ CircuitPython + -

 Welcome!

Welcome to CircuitPython's Web API. Go to the file browser to work with files in the CIRCUITPY drive. Make sure you've set CIRCUITPY_WEB_API_PASSWORD='somepassword' in /.env. Provide the password when the browser prompts for it. Leave the username blank. diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index cbe1b24e86..b9a1de67f2 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -86,6 +86,10 @@ typedef struct { bool authenticated; bool expect; bool json; + bool websocket; + uint32_t websocket_version; + // RFC6455 for websockets says this header should be 24 base64 characters long. + char websocket_key[24 + 1]; } _request; static wifi_radio_error_t wifi_status = WIFI_RADIO_ERROR_NONE; @@ -519,6 +523,7 @@ static void _reply_redirect(socketpool_socket_obj_t *socket, _request *request, "Location: http://", hostname, ".local", path, "\r\n", NULL); _cors_header(socket, request); _send_str(socket, "\r\n"); + ESP_LOGI(TAG, "redirect"); } static void _reply_directory_json(socketpool_socket_obj_t *socket, _request *request, FF_DIR *dir, const char *request_path, const char *path) { @@ -826,6 +831,8 @@ STATIC_FILE(directory_html); STATIC_FILE(directory_js); STATIC_FILE(welcome_html); STATIC_FILE(welcome_js); +STATIC_FILE(serial_html); +STATIC_FILE(serial_js); STATIC_FILE(blinka_16x16_ico); static void _reply_static(socketpool_socket_obj_t *socket, _request *request, const uint8_t *response, size_t response_len, const char *content_type) { @@ -844,6 +851,13 @@ static void _reply_static(socketpool_socket_obj_t *socket, _request *request, co #define _REPLY_STATIC(socket, request, filename) _reply_static(socket, request, filename, filename##_length, filename##_content_type) +static void _reply_websocket_upgrade(socketpool_socket_obj_t *socket, _request *request) { + ESP_LOGI(TAG, "websocket!"); + // Compute accept key + // Reply with upgrade + // Copy socket state into websocket and mark given socket as closed even though it isn't actually. +} + static bool _reply(socketpool_socket_obj_t *socket, _request *request) { if (request->redirect) { _reply_redirect(socket, request, request->path); @@ -980,6 +994,22 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { _reply_with_devices_json(socket, request); } else if (strcmp(path, "/version.json") == 0) { _reply_with_version_json(socket, request); + } else if (strcmp(path, "/serial/") == 0) { + if (!request->authenticated) { + if (_api_password[0] != '\0') { + _reply_unauthorized(socket, request); + } else { + _reply_forbidden(socket, request); + } + } else if (request->websocket) { + ESP_LOGI(TAG, "websocket!"); + _reply_websocket_upgrade(socket, request); + } else { + _REPLY_STATIC(socket, request, serial_html); + } + _reply_with_version_json(socket, request); + } else { + _reply_missing(socket, request); } } else if (strcmp(request->method, "GET") != 0) { _reply_method_not_allowed(socket, request); @@ -992,6 +1022,8 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { _REPLY_STATIC(socket, request, directory_js); } else if (strcmp(request->path, "/welcome.js") == 0) { _REPLY_STATIC(socket, request, welcome_js); + } else if (strcmp(request->path, "/serial.js") == 0) { + _REPLY_STATIC(socket, request, serial_js); } else if (strcmp(request->path, "/favicon.ico") == 0) { // TODO: Autogenerate this based on the blinka bitmap and change the // palette based on MAC address. @@ -1015,6 +1047,7 @@ static void _reset_request(_request *request) { request->authenticated = false; request->expect = false; request->json = false; + request->websocket = false; } static void _process_request(socketpool_socket_obj_t *socket, _request *request) { @@ -1111,6 +1144,13 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) strcpy(request->origin, request->header_value); } else if (strcmp(request->header_key, "X-Timestamp") == 0) { request->timestamp_ms = strtoull(request->header_value, NULL, 10); + } else if (strcmp(request->header_key, "Upgrade") == 0) { + request->websocket = strcmp(request->header_value, "websocket") == 0; + } else if (strcmp(request->header_key, "Sec-WebSocket-Version") == 0) { + request->websocket_version = strtoul(request->header_value, NULL, 10); + } else if (strcmp(request->header_key, "Sec-WebSocket-Key") == 0 && + strlen(request->header_value) == 24) { + strcpy(request->websocket_key, request->header_value); } ESP_LOGI(TAG, "Header %s %s", request->header_key, request->header_value); } else if (request->offset > sizeof(request->header_value) - 1) {