diff --git a/.github/workflows/cache_cleanup.yml b/.github/workflows/cache_cleanup.yml deleted file mode 100644 index 9d2859a75fc7..000000000000 --- a/.github/workflows/cache_cleanup.yml +++ /dev/null @@ -1,32 +0,0 @@ -# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy -name: 🧹 Cache Cleanup -on: - pull_request: - types: - - closed - -jobs: - cleanup: - name: Cleanup PR caches - runs-on: ubuntu-latest - permissions: - # `actions:write` permission is required to delete caches - actions: write - contents: read - steps: - - name: Cleanup - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GH_REPO: ${{ github.repository }} - BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge - run: | - echo "Fetching list of cache key" - cache_keys_for_pr=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id') - # Setting this to not fail the workflow while deleting cache keys. - set +e - echo "Deleting caches..." - for cache_key in $cache_keys_for_pr; do - gh cache delete $cache_key - echo "Deleted: $cache_key" - done - echo "Done" diff --git a/core/input/input.cpp b/core/input/input.cpp index 51f2c7f971f1..4dc8666b053a 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -656,10 +656,16 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_ int mapping = fallback_mapping; // Bypass the mapping system if the joypad's mapping is already handled by its driver // (for example, the SDL joypad driver). - if (!p_joypad_info.get("mapping_handled", false)) { + if (p_joypad_info.get("mapping_handled", false)) { + js.is_known = true; + } else { for (int i = 0; i < map_db.size(); i++) { if (js.uid == map_db[i].uid) { mapping = i; + if (mapping != fallback_mapping) { + js.is_known = true; + } + break; } } } @@ -1860,13 +1866,7 @@ void Input::set_fallback_mapping(const String &p_guid) { //platforms that use the remapping system can override and call to these ones bool Input::is_joy_known(int p_device) { - if (joy_names.has(p_device)) { - int mapping = joy_names[p_device].mapping; - if (mapping != -1 && mapping != fallback_mapping) { - return true; - } - } - return false; + return joy_names.has(p_device) && joy_names[p_device].is_known; } String Input::get_joy_guid(int p_device) const { diff --git a/core/input/input.h b/core/input/input.h index 66867b19582a..5f6e248c6d53 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -167,6 +167,7 @@ class Input : public Object { StringName name; StringName uid; bool connected = false; + bool is_known = false; bool last_buttons[(size_t)JoyButton::MAX] = { false }; float last_axis[(size_t)JoyAxis::MAX] = { 0.0f }; HatMask last_hat = HatMask::CENTER; diff --git a/core/io/image.cpp b/core/io/image.cpp index 099a0b1c1948..6d7aa8d8cacc 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -4230,14 +4230,18 @@ void Image::renormalize_uint8(uint8_t *p_rgb) { n += Vector3(1, 1, 1); n *= 0.5; n *= 255; - p_rgb[0] = CLAMP(int(n.x), 0, 255); - p_rgb[1] = CLAMP(int(n.y), 0, 255); - p_rgb[2] = CLAMP(int(n.z), 0, 255); + p_rgb[0] = CLAMP(int(Math::round(n.x)), 0, 255); + p_rgb[1] = CLAMP(int(Math::round(n.y)), 0, 255); + p_rgb[2] = CLAMP(int(Math::round(n.z)), 0, 255); } void Image::renormalize_float(float *p_rgb) { Vector3 n(p_rgb[0], p_rgb[1], p_rgb[2]); + n *= 2.0; + n -= Vector3(1, 1, 1); n.normalize(); + n += Vector3(1, 1, 1); + n *= 0.5; p_rgb[0] = n.x; p_rgb[1] = n.y; p_rgb[2] = n.z; @@ -4245,7 +4249,11 @@ void Image::renormalize_float(float *p_rgb) { void Image::renormalize_half(uint16_t *p_rgb) { Vector3 n(Math::half_to_float(p_rgb[0]), Math::half_to_float(p_rgb[1]), Math::half_to_float(p_rgb[2])); + n *= 2.0; + n -= Vector3(1, 1, 1); n.normalize(); + n += Vector3(1, 1, 1); + n *= 0.5; p_rgb[0] = Math::make_half_float(n.x); p_rgb[1] = Math::make_half_float(n.y); p_rgb[2] = Math::make_half_float(n.z); diff --git a/core/io/resource.cpp b/core/io/resource.cpp index f2ecdba930b0..621f6b456d7d 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -368,11 +368,14 @@ Ref Resource::_duplicate(const DuplicateParams &p_params) const { ERR_FAIL_COND_V_MSG(p_params.local_scene && p_params.subres_mode != RESOURCE_DEEP_DUPLICATE_MAX, Ref(), "Duplication for local-to-scene can't specify a deep duplicate mode."); DuplicateRemapCacheT *remap_cache_backup = thread_duplicate_remap_cache; + bool remap_cache_needs_deallocation_backup = thread_duplicate_remap_cache_needs_deallocation; // These are for avoiding potential duplicates that can happen in custom code // from participating in the same duplication session (remap cache). #define BEFORE_USER_CODE thread_duplicate_remap_cache = nullptr; -#define AFTER_USER_CODE thread_duplicate_remap_cache = remap_cache_backup; +#define AFTER_USER_CODE \ + thread_duplicate_remap_cache = remap_cache_backup; \ + thread_duplicate_remap_cache_needs_deallocation = remap_cache_needs_deallocation_backup; List plist; get_property_list(&plist); @@ -434,7 +437,9 @@ Ref Resource::duplicate_for_local_scene(Node *p_for_scene, DuplicateRe #endif DuplicateRemapCacheT *remap_cache_backup = thread_duplicate_remap_cache; + bool remap_cache_needs_deallocation_backup = thread_duplicate_remap_cache_needs_deallocation; thread_duplicate_remap_cache = &p_remap_cache; + thread_duplicate_remap_cache_needs_deallocation = false; DuplicateParams params; params.deep = true; @@ -442,6 +447,7 @@ Ref Resource::duplicate_for_local_scene(Node *p_for_scene, DuplicateRe const Ref &dupe = _duplicate(params); thread_duplicate_remap_cache = remap_cache_backup; + thread_duplicate_remap_cache_needs_deallocation = remap_cache_needs_deallocation_backup; return dupe; } @@ -504,6 +510,7 @@ Ref Resource::duplicate(bool p_deep) const { bool started_session = false; if (!thread_duplicate_remap_cache) { thread_duplicate_remap_cache = &remap_cache; + thread_duplicate_remap_cache_needs_deallocation = false; started_session = true; } @@ -526,6 +533,7 @@ Ref Resource::duplicate_deep(ResourceDeepDuplicateMode p_deep_subresou bool started_session = false; if (!thread_duplicate_remap_cache) { thread_duplicate_remap_cache = &remap_cache; + thread_duplicate_remap_cache_needs_deallocation = false; started_session = true; } @@ -571,6 +579,7 @@ Ref Resource::_duplicate_from_variant(bool p_deep, ResourceDeepDuplica } } else { thread_duplicate_remap_cache = memnew(DuplicateRemapCacheT); + thread_duplicate_remap_cache_needs_deallocation = true; } DuplicateParams params; @@ -583,7 +592,7 @@ Ref Resource::_duplicate_from_variant(bool p_deep, ResourceDeepDuplica } void Resource::_teardown_duplicate_from_variant() { - if (thread_duplicate_remap_cache) { + if (thread_duplicate_remap_cache && thread_duplicate_remap_cache_needs_deallocation) { memdelete(thread_duplicate_remap_cache); thread_duplicate_remap_cache = nullptr; } diff --git a/core/io/resource.h b/core/io/resource.h index 090d2248e2c8..0ed6d260e2c0 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -92,6 +92,7 @@ class Resource : public RefCounted { using DuplicateRemapCacheT = HashMap, Ref>; static thread_local inline DuplicateRemapCacheT *thread_duplicate_remap_cache = nullptr; + static thread_local inline bool thread_duplicate_remap_cache_needs_deallocation = true; Variant _duplicate_recursive(const Variant &p_variant, const DuplicateParams &p_params, uint32_t p_usage = 0) const; void _find_sub_resources(const Variant &p_variant, HashSet> &p_resources_found); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 813be0928b82..b44f990c0c50 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -697,10 +697,17 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const } String local_path = _validate_local_path(p_path); - ERR_FAIL_COND_V_MSG(!thread_load_tasks.has(local_path), THREAD_LOAD_INVALID_RESOURCE, "Bug in ResourceLoader logic, please report."); + LoadToken *load_token = user_load_tokens[p_path]; + ThreadLoadTask *load_task_ptr; + + if (load_token->task_if_unregistered) { + load_task_ptr = load_token->task_if_unregistered; + } else { + ERR_FAIL_COND_V_MSG(!thread_load_tasks.has(local_path), THREAD_LOAD_INVALID_RESOURCE, "Bug in ResourceLoader logic, please report."); + load_task_ptr = &thread_load_tasks[local_path]; + } - ThreadLoadTask &load_task = thread_load_tasks[local_path]; - status = load_task.status; + status = load_task_ptr->status; if (r_progress) { *r_progress = _dependency_get_progress(local_path); } @@ -708,10 +715,10 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const // Support userland polling in a loop on the main thread. if (Thread::is_main_thread() && status == THREAD_LOAD_IN_PROGRESS) { uint64_t frame = Engine::get_singleton()->get_process_frames(); - if (frame == load_task.last_progress_check_main_thread_frame) { + if (frame == load_task_ptr->last_progress_check_main_thread_frame) { ensure_progress = true; } else { - load_task.last_progress_check_main_thread_frame = frame; + load_task_ptr->last_progress_check_main_thread_frame = frame; } } } @@ -745,8 +752,23 @@ Ref ResourceLoader::load_threaded_get(const String &p_path, Error *r_e // Support userland requesting on the main thread before the load is reported to be complete. if (Thread::is_main_thread() && !load_token->local_path.is_empty()) { - const ThreadLoadTask &load_task = thread_load_tasks[load_token->local_path]; - while (load_task.status == THREAD_LOAD_IN_PROGRESS) { + ThreadLoadTask *load_task_ptr; + + if (load_token->task_if_unregistered) { + load_task_ptr = load_token->task_if_unregistered; + } else { + if (!thread_load_tasks.has(load_token->local_path)) { + print_error("Bug in ResourceLoader logic, please report."); + if (r_error) { + *r_error = ERR_BUG; + } + return Ref(); + } + + load_task_ptr = &thread_load_tasks[load_token->local_path]; + } + + while (load_task_ptr->status == THREAD_LOAD_IN_PROGRESS) { thread_load_lock.temp_unlock(); bool exit = !_ensure_load_progress(); OS::get_singleton()->delay_usec(1000); diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 7b3fd0948dfa..795828ac8b1f 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -134,7 +134,10 @@ ResourceUID::ID ResourceUID::create_id_for_path(const String &p_path) { RandomPCG rng; const String project_name = GLOBAL_GET("application/config/name"); - rng.seed(project_name.hash64() * p_path.hash64() * FileAccess::get_md5(p_path).hash64()); + // Use lowercase file name as random seed. + // This ensures that case differences don't cause UIDs to shift on case-insensitive filesystems. The downside is that identical files with different case + // (but otherwise identical name) will run into a hash collision, but this is a very rare scenario. + rng.seed(project_name.hash64() * p_path.to_lower().hash64() * FileAccess::get_md5(p_path).hash64()); while (true) { int64_t num1 = rng.rand(); diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 9a04e5c00f48..7083050caa99 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -5524,7 +5524,11 @@ String String::sprintf(const Array &values, bool *error) const { // Get basic number. String str; if (!as_unsigned) { - str = String::num_int64(Math::abs(value), base, capitalize); + if (value == INT64_MIN) { // INT64_MIN can't be represented as positive value. + str = String::num_int64(value, base, capitalize).trim_prefix("-"); + } else { + str = String::num_int64(Math::abs(value), base, capitalize); + } } else { uint64_t uvalue = *((uint64_t *)&value); // In unsigned hex, if the value fits in 32 bits, trim it down to that. diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 1501a2f3d66f..706e42df1ad2 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -6,6 +6,7 @@ Object that holds the project-independent editor settings. These settings are generally visible in the [b]Editor > Editor Settings[/b] menu. Property names use slash delimiters to distinguish sections. Setting values can be of any [Variant] type. It's recommended to use [code]snake_case[/code] for editor settings to be consistent with the Godot editor itself. + Editor settings are saved automatically when changed. Accessing the settings can be done using the following methods, such as: [codeblocks] [gdscript] diff --git a/drivers/sdl/SDL_build_config_private.h b/drivers/sdl/SDL_build_config_private.h index 7c18933e13f8..ed0cc09c5ba3 100644 --- a/drivers/sdl/SDL_build_config_private.h +++ b/drivers/sdl/SDL_build_config_private.h @@ -81,6 +81,7 @@ #define SDL_PLATFORM_UNIX 1 #define HAVE_STDIO_H 1 +#define HAVE_LIBC 1 #define HAVE_LINUX_INPUT_H 1 #define HAVE_POLL 1 diff --git a/drivers/sdl/joypad_sdl.cpp b/drivers/sdl/joypad_sdl.cpp index 85e43be7554a..c153d2451f4e 100644 --- a/drivers/sdl/joypad_sdl.cpp +++ b/drivers/sdl/joypad_sdl.cpp @@ -188,7 +188,8 @@ void JoypadSDL::process_events() { sdl_instance_id_to_joypad_id.insert(sdl_event.jdevice.which, joy_id); Dictionary joypad_info; - joypad_info["mapping_handled"] = true; // Skip Godot's mapping system because SDL already handles the joypad's mapping. + // Skip Godot's mapping system if SDL already handles the joypad's mapping. + joypad_info["mapping_handled"] = SDL_IsGamepad(sdl_event.jdevice.which); joypad_info["raw_name"] = String(SDL_GetJoystickName(joy)); joypad_info["vendor_id"] = itos(SDL_GetJoystickVendor(joy)); joypad_info["product_id"] = itos(SDL_GetJoystickProduct(joy)); diff --git a/editor/file_system/editor_file_system.cpp b/editor/file_system/editor_file_system.cpp index d94aae026337..4df5ae7f0ad3 100644 --- a/editor/file_system/editor_file_system.cpp +++ b/editor/file_system/editor_file_system.cpp @@ -3086,7 +3086,7 @@ Error EditorFileSystem::_copy_file(const String &p_from, const String &p_to) { } // Roll a new uid for this copied .import file to avoid conflict. - ResourceUID::ID res_uid = ResourceUID::get_singleton()->create_id(); + ResourceUID::ID res_uid = ResourceUID::get_singleton()->create_id_for_path(p_to); // Save the new .import file Ref cfg; diff --git a/editor/run/editor_run.cpp b/editor/run/editor_run.cpp index 059032cf74ac..f3b19a2b3406 100644 --- a/editor/run/editor_run.cpp +++ b/editor/run/editor_run.cpp @@ -167,10 +167,12 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie, const V } if (OS::get_singleton()->is_stdout_verbose()) { - print_line(vformat("Running: %s", exec)); + PackedStringArray output; + output.append(vformat("Running: %s", exec)); for (const String &E : instance_args) { - print_line(" %s", E); + output.append(E); } + print_line(String(" ").join(output)); } OS::ProcessID pid = 0; diff --git a/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.cpp index 83255456c996..6b89245f9002 100644 --- a/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.cpp @@ -76,7 +76,7 @@ bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_s } bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get(const StringName &p_name, Variant &r_ret) const { - if (!tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_null()) { return false; } String name = p_name; @@ -114,7 +114,7 @@ void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::ed } // Disconnect to changes. - if (tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_valid()) { tile_set_scenes_collection_source->disconnect(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed)); } @@ -123,7 +123,7 @@ void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::ed source_id = p_source_id; // Connect to changes. - if (tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_valid()) { if (!tile_set_scenes_collection_source->is_connected(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed))) { tile_set_scenes_collection_source->connect(CoreStringName(property_list_changed), callable_mp((Object *)this, &Object::notify_property_list_changed)); } @@ -134,7 +134,7 @@ void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::ed // -- Proxy object used by the tile inspector -- bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_set(const StringName &p_name, const Variant &p_value) { - if (!tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_null()) { return false; } @@ -166,7 +166,7 @@ bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_set(const Strin } bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get(const StringName &p_name, Variant &r_ret) const { - if (!tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_null()) { return false; } @@ -185,7 +185,7 @@ bool TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get(const Strin } void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::_get_property_list(List *p_list) const { - if (!tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_null()) { return; } @@ -257,8 +257,8 @@ void TileSetScenesCollectionSourceEditor::_scene_file_selected(const String &p_p int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add a Scene Tile")); - undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", scene, scene_id); - undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); + undo_redo->add_do_method(*tile_set_scenes_collection_source, "create_scene_tile", scene, scene_id); + undo_redo->add_undo_method(*tile_set_scenes_collection_source, "remove_scene_tile", scene_id); undo_redo->commit_action(); _update_scenes_list(); _update_action_buttons(); @@ -272,8 +272,8 @@ void TileSetScenesCollectionSourceEditor::_source_delete_pressed() { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Remove a Scene Tile")); - undo_redo->add_do_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); - undo_redo->add_undo_method(tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id); + undo_redo->add_do_method(*tile_set_scenes_collection_source, "remove_scene_tile", scene_id); + undo_redo->add_undo_method(*tile_set_scenes_collection_source, "create_scene_tile", tile_set_scenes_collection_source->get_scene_tile_scene(scene_id), scene_id); undo_redo->commit_action(); _update_scenes_list(); _update_action_buttons(); @@ -282,7 +282,7 @@ void TileSetScenesCollectionSourceEditor::_source_delete_pressed() { void TileSetScenesCollectionSourceEditor::_update_source_inspector() { // Update the proxy object. - scenes_collection_source_proxy_object->edit(tile_set, tile_set_scenes_collection_source, tile_set_source_id); + scenes_collection_source_proxy_object->edit(tile_set, *tile_set_scenes_collection_source, tile_set_source_id); } void TileSetScenesCollectionSourceEditor::_update_tile_inspector() { @@ -292,7 +292,7 @@ void TileSetScenesCollectionSourceEditor::_update_tile_inspector() { // Update the proxy object. if (has_atlas_tile_selected) { int scene_id = scene_tiles_list->get_item_metadata(selected_indices[0]); - tile_proxy_object->edit(tile_set_scenes_collection_source, scene_id); + tile_proxy_object->edit(*tile_set_scenes_collection_source, scene_id); } // Update visibility. @@ -306,7 +306,7 @@ void TileSetScenesCollectionSourceEditor::_update_action_buttons() { } void TileSetScenesCollectionSourceEditor::_update_scenes_list() { - if (!tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_null()) { return; } @@ -408,12 +408,12 @@ void TileSetScenesCollectionSourceEditor::edit(Ref p_tile_set, TileSetS new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); } - if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) { + if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == *tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) { return; } // Remove listener for old objects. - if (tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_valid()) { tile_set_scenes_collection_source->disconnect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); } @@ -433,7 +433,7 @@ void TileSetScenesCollectionSourceEditor::edit(Ref p_tile_set, TileSetS } // Add the listener again. - if (tile_set_scenes_collection_source) { + if (tile_set_scenes_collection_source.is_valid()) { tile_set_scenes_collection_source->connect_changed(callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); } @@ -459,8 +459,8 @@ void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, c int scene_id = tile_set_scenes_collection_source->get_next_scene_tile_id(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add a Scene Tile")); - undo_redo->add_do_method(tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id); - undo_redo->add_undo_method(tile_set_scenes_collection_source, "remove_scene_tile", scene_id); + undo_redo->add_do_method(*tile_set_scenes_collection_source, "create_scene_tile", resource, scene_id); + undo_redo->add_undo_method(*tile_set_scenes_collection_source, "remove_scene_tile", scene_id); undo_redo->commit_action(); } } diff --git a/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.h b/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.h index 11271873cd19..46e199aba75d 100644 --- a/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.h +++ b/editor/scene/2d/tiles/tile_set_scenes_collection_source_editor.h @@ -49,7 +49,7 @@ class TileSetScenesCollectionSourceEditor : public HBoxContainer { private: Ref tile_set; - TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; + Ref tile_set_scenes_collection_source; int source_id = -1; protected: @@ -72,7 +72,7 @@ class TileSetScenesCollectionSourceEditor : public HBoxContainer { private: TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr; - TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; + Ref tile_set_scenes_collection_source; int source_id; int scene_id; @@ -96,7 +96,7 @@ class TileSetScenesCollectionSourceEditor : public HBoxContainer { bool read_only = false; Ref tile_set; - TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; + Ref tile_set_scenes_collection_source; int tile_set_source_id = -1; bool tile_set_scenes_collection_source_changed_needs_update = false; diff --git a/editor/settings/editor_settings_dialog.cpp b/editor/settings/editor_settings_dialog.cpp index 5f500ae19aff..013a7c60cedf 100644 --- a/editor/settings/editor_settings_dialog.cpp +++ b/editor/settings/editor_settings_dialog.cpp @@ -62,9 +62,7 @@ void EditorSettingsDialog::ok_pressed() { } void EditorSettingsDialog::_settings_changed() { - if (is_visible()) { - timer->start(); - } + timer->start(); } void EditorSettingsDialog::_settings_property_edited(const String &p_name) { diff --git a/main/main.cpp b/main/main.cpp index 7ab3b8360387..a6e925ae3072 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1807,7 +1807,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else if (arg == "--editor-pseudolocalization") { editor_pseudolocalization = true; #endif // TOOLS_ENABLED - } else if (arg == "--profile-gpu") { + } else if (arg == "--gpu-profile") { profile_gpu = true; } else if (arg == "--disable-crash-handler") { OS::get_singleton()->disable_crash_handler(); @@ -4626,7 +4626,21 @@ int Main::start() { } if (movie_writer) { - movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path()); + Size2i movie_size = Size2i(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + String stretch_mode = GLOBAL_GET("display/window/stretch/mode"); + if (stretch_mode != "viewport") { + // `canvas_items` and `disabled` modes use the window size override instead, + // which allows for higher resolution recording with 2D elements designed for a lower resolution. + const int window_width_override = GLOBAL_GET("display/window/size/window_width_override"); + if (window_width_override > 0) { + movie_size.width = window_width_override; + } + const int window_height_override = GLOBAL_GET("display/window/size/window_height_override"); + if (window_height_override > 0) { + movie_size.height = window_height_override; + } + } + movie_writer->begin(movie_size, fixed_fps, Engine::get_singleton()->get_write_movie_path()); } GDExtensionManager::get_singleton()->startup(); diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index eaaa4bb2705e..39bc49ab2df5 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -500,8 +500,8 @@ Array GDScriptTextDocument::find_symbols(const LSP::TextDocumentPositionParams & if (file_checker->file_exists(path)) { arr.push_back(location.to_json()); } - r_list.push_back(symbol); } + r_list.push_back(symbol); } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { List list; GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list); diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj index f807132509e9..eee61077b2ff 100644 --- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj @@ -1,8 +1,8 @@  {6CE9A984-37B1-4F8A-8FE9-609F05F071B3} - net6.0 - 10 + net8.0 + 12 enable diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj index 0174b25b3ffa..1b86e58f9e27 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj @@ -2,8 +2,8 @@ {B06C2951-C8E3-4F28-80B2-717CF327EB19} Exe - net6.0 - 10 + net8.0 + 12 enable diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IHandshake.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IHandshake.cs index 8ec60876ccb6..66ba50322fcd 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IHandshake.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IHandshake.cs @@ -4,7 +4,7 @@ namespace GodotTools.IdeMessaging { public interface IHandshake { - string GetHandshakeLine(string identity); - bool IsValidPeerHandshake(string handshake, [NotNullWhen(true)] out string? identity, ILogger logger); + public string GetHandshakeLine(string identity); + public bool IsValidPeerHandshake(string handshake, [NotNullWhen(true)] out string? identity, ILogger logger); } } diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ILogger.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ILogger.cs index d2855f93a136..2df480cc191f 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ILogger.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/ILogger.cs @@ -4,10 +4,10 @@ namespace GodotTools.IdeMessaging { public interface ILogger { - void LogDebug(string message); - void LogInfo(string message); - void LogWarning(string message); - void LogError(string message); - void LogError(string message, Exception e); + public void LogDebug(string message); + public void LogInfo(string message); + public void LogWarning(string message); + public void LogError(string message); + public void LogError(string message, Exception e); } } diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IMessageHandler.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IMessageHandler.cs index 9622fcc96db4..08abf9a51e9a 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IMessageHandler.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/IMessageHandler.cs @@ -4,6 +4,6 @@ namespace GodotTools.IdeMessaging { public interface IMessageHandler { - Task HandleRequest(Peer peer, string id, MessageContent content, ILogger logger); + public Task HandleRequest(Peer peer, string id, MessageContent content, ILogger logger); } } diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj index 208e6d8f4119..406814bd2e5c 100644 --- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj @@ -2,8 +2,8 @@ {EAFFF236-FA96-4A4D-BD23-0E51EF988277} Exe - net6.0-windows - 10 + net8.0-windows + 12 enable False LatestMajor diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs index 5bf07f626bac..ec3c8d283dfa 100644 --- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs +++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs @@ -46,19 +46,13 @@ private static int Main(string[] args) if (dte == null) { - // Open a new instance - dte = TryVisualStudioLaunch("VisualStudio.DTE.17.0"); + dte = TryVisualStudioLaunch("VisualStudio.DTE.18.0") // Visual Studio 18 (2026). + ?? TryVisualStudioLaunch("VisualStudio.DTE.17.0"); // Visual Studio 17 (2022). if (dte == null) { - // Launch of VS 2022 failed, fallback to 2019 - dte = TryVisualStudioLaunch("VisualStudio.DTE.16.0"); - - if (dte == null) - { - Console.Error.WriteLine("Visual Studio not found"); - return 1; - } + Console.Error.WriteLine("Could not find a supported Visual Studio version (VS 2026 or VS 2022)."); + return 1; } dte.UserControl = true; @@ -188,8 +182,8 @@ private static int Main(string[] args) if (ppszDisplayName == null) continue; - // The digits after the colon are the process ID - if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.1[6-7].0:[0-9]")) + // The digits after the colon are the process ID. + if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.1[7-8].0:[0-9]")) continue; if (pprot.GetObject(moniker[0], out object ppunkObject) == 0) @@ -281,13 +275,13 @@ int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dw private interface IOleMessageFilter { [PreserveSig] - int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); + public int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); [PreserveSig] - int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); + public int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); [PreserveSig] - int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); + public int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); } #endregion diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 0ed36d43ed0b..3a08e8d36806 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -250,7 +250,8 @@ internal static unsafe void GetGlobalClassName(godot_string* scriptPath, godot_s } if (!foundGlobalBaseScript) { - *outBaseType = Marshaling.ConvertStringToNative(native.Name); + string nativeName = native.GetCustomAttribute(false)?.Name ?? native.Name; + *outBaseType = Marshaling.ConvertStringToNative(nativeName); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs index 3288705dab82..6cb6de86ec91 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs @@ -10,12 +10,12 @@ public interface ISerializationListener /// Executed before serializing this instance's state when reloading assemblies. /// Clear any data that should not be serialized. /// - void OnBeforeSerialize(); + public void OnBeforeSerialize(); /// /// Executed after deserializing this instance's state after reloading assemblies. /// Restore any state that has been lost. /// - void OnAfterDeserialize(); + public void OnAfterDeserialize(); } } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e038cb8d4ae5..fc19f264c1b4 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -6814,6 +6814,22 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star unsigned int last_cluster_index = 0; bool last_cluster_valid = true; + unsigned int last_non_zero_w = glyph_count - 1; + if (last_run) { + for (int64_t i = glyph_count - 1; i >= 0; i--) { + last_non_zero_w = (unsigned int)i; + if (p_sd->orientation == ORIENTATION_HORIZONTAL) { + if (glyph_pos[i].x_advance != 0) { + break; + } + } else { + if (glyph_pos[i].y_advance != 0) { + break; + } + } + } + } + double adv_rem = 0.0; for (unsigned int i = 0; i < glyph_count; i++) { if ((i > 0) && (last_cluster_id != glyph_info[i].cluster)) { @@ -6905,8 +6921,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star gl.x_off += _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size)); } } - if (!last_run || i < glyph_count - 1) { - // Do not add extra spacing to the last glyph of the string. + if ((!last_run || i < last_non_zero_w) && !Math::is_zero_approx(gl.advance)) { + // Do not add extra spacing to the last glyph of the string and zero width glyphs. if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) { gl.advance += sp_sp; } else { diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 9c3d968e6033..aec0ce33f820 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -4816,6 +4816,17 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { sd->glyphs.push_back(gl); } else { // Text span. + int last_non_zero_w = sd->end - 1; + if (i == sd->spans.size() - 1) { + for (int j = span.end - 1; j >= span.start; j--) { + last_non_zero_w = j; + uint32_t idx = (int32_t)sd->text[j - sd->start]; + if (!is_control(idx) && !(idx >= 0x200B && idx <= 0x200D)) { + break; + } + } + } + RID prev_font; for (int j = span.start; j < span.end; j++) { Glyph gl; @@ -4825,7 +4836,8 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { gl.count = 1; gl.font_size = span.font_size; gl.index = (int32_t)sd->text[j - sd->start]; // Use codepoint. - if (gl.index == 0x0009 || gl.index == 0x000b) { + bool zw = (gl.index >= 0x200b && gl.index <= 0x200d); + if (gl.index == 0x0009 || gl.index == 0x000b || zw) { gl.index = 0x0020; } if (!sd->preserve_control && is_control(gl.index)) { @@ -4871,8 +4883,11 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { sd->descent = MAX(sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5)); } } - if (j < sd->end - 1) { - // Do not add extra spacing to the last glyph of the string. + if (zw) { + gl.advance = 0.0; + } + if ((j < last_non_zero_w) && !Math::is_zero_approx(gl.advance)) { + // Do not add extra spacing to the last glyph of the string and zero width glyphs. if (is_whitespace(sd->text[j - sd->start])) { gl.advance += sd->extra_spacing[SPACING_SPACE] + _font_get_spacing(gl.font_rid, SPACING_SPACE); } else { diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp index 969297d1e74b..c5c184099437 100644 --- a/modules/tinyexr/image_saver_tinyexr.cpp +++ b/modules/tinyexr/image_saver_tinyexr.cpp @@ -34,6 +34,7 @@ #include "core/math/math_funcs.h" #include // Should come before including tinyexr. +#include #include "thirdparty/tinyexr/tinyexr.h" diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index e040d0ec0e07..279bed93c7d8 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -268,11 +268,14 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe AnimationMixer::PlaybackInfo pi = p_playback_info; pi.start = 0.0; pi.end = cur_len; - if (node_backward ? cur_backward : !cur_backward) { + if (play_mode == PLAY_MODE_FORWARD) { pi.time = cur_playback_time; - pi.delta = cur_delta; } else { pi.time = anim_size - cur_playback_time; + } + if (node_backward ? cur_backward : !cur_backward) { + pi.delta = cur_delta; + } else { pi.delta = -cur_delta; } pi.weight = 1.0; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 30991f05aea8..8bb548e815ee 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -73,15 +73,8 @@ Size2 ScrollContainer::get_minimum_size() const { } } - Size2 panel_size = theme_cache.panel_style->get_minimum_size(); - min_size += panel_size; - if (draw_focus_border) { - Size2 focus_size = theme_cache.focus_style->get_minimum_size(); - // Only update the minimum size if the focus style's minimum size doesn't fit into the panel style's minimum size. - if (focus_size > panel_size) { - min_size += focus_size - panel_size; - } - } + Rect2 margins = _get_margins(); + min_size += margins.position + margins.size; return min_size; } @@ -111,6 +104,34 @@ bool ScrollContainer::_is_v_scroll_visible() const { return v_scroll->is_visible() && v_scroll->get_parent() == this; } +Rect2 ScrollContainer::_get_margins() const { + float right_margin = theme_cache.panel_style->get_margin(SIDE_RIGHT); + float left_margin = theme_cache.panel_style->get_margin(SIDE_LEFT); + float top_margin = theme_cache.panel_style->get_margin(SIDE_TOP); + float bottom_margin = theme_cache.panel_style->get_margin(SIDE_BOTTOM); + if (draw_focus_border) { + // Only update margins if the focus style's margins don't fit into the panel style's margins. + float focus_margin = theme_cache.focus_style->get_margin(SIDE_RIGHT); + if (focus_margin > right_margin) { + right_margin = focus_margin; + } + focus_margin = theme_cache.focus_style->get_margin(SIDE_LEFT); + if (focus_margin > left_margin) { + left_margin = focus_margin; + } + focus_margin = theme_cache.focus_style->get_margin(SIDE_TOP); + if (focus_margin > top_margin) { + top_margin = focus_margin; + } + focus_margin = theme_cache.focus_style->get_margin(SIDE_BOTTOM); + if (focus_margin > bottom_margin) { + bottom_margin = focus_margin; + } + } + + return Rect2(left_margin, top_margin, right_margin, bottom_margin); +} + void ScrollContainer::gui_input(const Ref &p_gui_input) { ERR_FAIL_COND(p_gui_input.is_null()); @@ -268,45 +289,23 @@ void ScrollContainer::_update_scrollbar_position() { return; } - float right_margin = theme_cache.panel_style->get_margin(SIDE_RIGHT); - float left_margin = theme_cache.panel_style->get_margin(SIDE_LEFT); - float top_margin = theme_cache.panel_style->get_margin(SIDE_TOP); - float bottom_margin = theme_cache.panel_style->get_margin(SIDE_BOTTOM); - if (draw_focus_border) { - // Only update margins if the focus style's margins don't fit into the panel style's margins. - float focus_margin = theme_cache.focus_style->get_margin(SIDE_RIGHT); - if (focus_margin > right_margin) { - right_margin += focus_margin; - } - focus_margin = theme_cache.focus_style->get_margin(SIDE_LEFT); - if (focus_margin > left_margin) { - left_margin += focus_margin; - } - focus_margin = theme_cache.focus_style->get_margin(SIDE_TOP); - if (focus_margin > top_margin) { - top_margin += focus_margin; - } - focus_margin = theme_cache.focus_style->get_margin(SIDE_BOTTOM); - if (focus_margin > bottom_margin) { - bottom_margin += focus_margin; - } - } + Rect2 margins = _get_margins(); Size2 hmin = h_scroll->is_visible() ? h_scroll->get_combined_minimum_size() : Size2(); Size2 vmin = v_scroll->is_visible() ? v_scroll->get_combined_minimum_size() : Size2(); - int lmar = is_layout_rtl() ? right_margin : left_margin; - int rmar = is_layout_rtl() ? left_margin : right_margin; + int lmar = is_layout_rtl() ? margins.size.x : margins.position.x; + int rmar = is_layout_rtl() ? margins.position.x : margins.size.x; h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, lmar); h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -rmar - vmin.width); - h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height - bottom_margin); - h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bottom_margin); + h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height - margins.size.y); + h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -margins.size.y); v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width - rmar); v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -rmar); - v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, top_margin); - v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -hmin.height - bottom_margin); + v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, margins.position.y); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -hmin.height - margins.size.y); _updating_scrollbars = false; } @@ -344,24 +343,11 @@ void ScrollContainer::ensure_control_visible(Control *p_control) { void ScrollContainer::_reposition_children() { update_scrollbars(); - Size2 size = get_size(); - Point2 ofs; - Size2 panel_size = theme_cache.panel_style->get_minimum_size(); - Point2 panel_offset = theme_cache.panel_style->get_offset(); - size -= panel_size; - ofs += panel_offset; - if (draw_focus_border) { - // Only update the size and offset if focus style's doesn't fit into the panel style's. - Size2 focus_size = theme_cache.focus_style->get_minimum_size(); - if (focus_size > panel_size) { - size -= focus_size - panel_size; - } - Point2 focus_offset = theme_cache.focus_style->get_offset(); - if (focus_offset > panel_offset) { - ofs += focus_offset - panel_offset; - } - } + Rect2 margins = _get_margins(); + Size2 size = get_size(); + size -= margins.position + margins.size; + Point2 ofs = margins.position; bool rtl = is_layout_rtl(); bool reserve_vscroll = _is_v_scroll_visible() || vertical_scroll_mode == SCROLL_MODE_RESERVE; @@ -594,16 +580,10 @@ void ScrollContainer::_notification(int p_what) { } void ScrollContainer::update_scrollbars() { + Rect2 margins = _get_margins(); + Size2 size = get_size(); - Size2 panel_size = theme_cache.panel_style->get_minimum_size(); - size -= panel_size; - if (draw_focus_border) { - Size2 focus_size = theme_cache.focus_style->get_minimum_size(); - // Only update the size if the focus style's minimum size doesn't fit into the panel style's minimum size. - if (focus_size > panel_size) { - size -= focus_size - panel_size; - } - } + size -= margins.position + margins.size; Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 1970ed403fc1..47b39daca7ad 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -85,6 +85,8 @@ class ScrollContainer : public Container { bool _is_h_scroll_visible() const; bool _is_v_scroll_visible() const; + Rect2 _get_margins() const; + bool draw_focus_border = false; bool focus_border_is_drawn = false; bool child_has_focus(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 7bad977132fb..b24c0590d70a 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2914,6 +2914,10 @@ Node *Node::duplicate(int p_flags) const { ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node."); + if (p_flags & DUPLICATE_SCRIPTS) { + _duplicate_scripts(this, dupe); + } + _duplicate_properties(this, this, dupe, p_flags); if (p_flags & DUPLICATE_SIGNALS) { @@ -2934,6 +2938,10 @@ Node *Node::duplicate_from_editor(HashMap &r_duplimap, con ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node."); + if (flags & DUPLICATE_SCRIPTS) { + _duplicate_scripts(this, dupe); + } + _duplicate_properties(this, this, dupe, flags); // This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated. @@ -3006,6 +3014,20 @@ void Node::_emit_editor_state_changed() { } #endif +void Node::_duplicate_scripts(const Node *p_original, Node *p_copy) const { + bool is_valid = false; + Variant scr = p_original->get(CoreStringName(script), &is_valid); + if (is_valid) { + p_copy->set(CoreStringName(script), scr); + } + + for (int i = 0; i < p_original->get_child_count(false); i++) { + Node *copy_child = p_copy->get_child(i, false); + ERR_FAIL_NULL_MSG(copy_child, "Child node disappeared while duplicating."); + _duplicate_scripts(p_original->get_child(i, false), copy_child); + } +} + // Duplicate node's properties. // This has to be called after nodes have been duplicated since there might be properties // of type Node that can be updated properly only if duplicated node tree is complete. @@ -3013,13 +3035,6 @@ void Node::_duplicate_properties(const Node *p_root, const Node *p_original, Nod List props; p_original->get_property_list(&props); const StringName &script_property_name = CoreStringName(script); - if (p_flags & DUPLICATE_SCRIPTS) { - bool is_valid = false; - Variant scr = p_original->get(script_property_name, &is_valid); - if (is_valid) { - p_copy->set(script_property_name, scr); - } - } for (const PropertyInfo &E : props) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; diff --git a/scene/main/node.h b/scene/main/node.h index 8276b4715f58..8b99b0952759 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -287,6 +287,7 @@ class Node : public Object { void _propagate_translation_domain_dirty(); Array _get_node_and_resource(const NodePath &p_path); + void _duplicate_scripts(const Node *p_original, Node *p_copy) const; void _duplicate_properties(const Node *p_root, const Node *p_original, Node *p_copy, int p_flags) const; void _duplicate_signals(const Node *p_original, Node *p_copy) const; Node *_duplicate(int p_flags, HashMap *r_duplimap = nullptr) const; diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 1b4ad6bd5591..762494e62f80 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -37,6 +37,8 @@ #include "scene/main/scene_tree_fti.h" #include "scene/resources/mesh.h" +#include + #undef Window class PackedScene; diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp index bba7c56c9f99..02347f4a9331 100644 --- a/servers/movie_writer/movie_writer.cpp +++ b/servers/movie_writer/movie_writer.cpp @@ -97,20 +97,9 @@ void MovieWriter::get_supported_extensions(List *r_extensions) const { void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) { project_name = GLOBAL_GET("application/config/name"); + movie_size = p_movie_size; - print_line(vformat("Movie Maker mode enabled, recording movie at %d FPS...", p_fps)); - - // When using Display/Window/Stretch/Mode = Viewport, use the project's - // configured viewport size instead of the size of the window in the OS - Size2i actual_movie_size = p_movie_size; - String stretch_mode = GLOBAL_GET("display/window/stretch/mode"); - if (stretch_mode == "viewport") { - actual_movie_size.width = GLOBAL_GET("display/window/size/viewport_width"); - actual_movie_size.height = GLOBAL_GET("display/window/size/viewport_height"); - - print_line(vformat("Movie Maker mode using project viewport size: %dx%d", - actual_movie_size.width, actual_movie_size.height)); - } + print_line(vformat(U"Movie Maker mode enabled, recording movie in %s×%s @ %d FPS...", movie_size.width, movie_size.height, p_fps)); // Check for available disk space and warn the user if needed. Ref dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -139,7 +128,7 @@ void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String audio_channels = AudioDriverDummy::get_dummy_singleton()->get_channels(); audio_mix_buffer.resize(mix_rate * audio_channels / fps); - write_begin(actual_movie_size, p_fps, p_base_path); + write_begin(movie_size, p_fps, p_base_path); } void MovieWriter::_bind_methods() { @@ -205,6 +194,37 @@ void MovieWriter::add_frame() { RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID); RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid); Ref vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture); + + if (vp_tex->get_size() != movie_size) { + // Resize the texture to the output resolution if it differs from the current viewport size. + // This ensures all frames have the same resolution, as not all video formats and players + // support resolution changes during playback. + + const float src_aspect = vp_tex->get_size().aspect(); + const float dst_aspect = movie_size.aspect(); + + int crop_width = vp_tex->get_size().width; + int crop_height = vp_tex->get_size().height; + int crop_x = 0; + int crop_y = 0; + + // If the aspect ratio differs, crop the image to cover the base resolution's aspect ratio + // in a way similar to `TextureRect.STRETCH_KEEP_ASPECT_COVERED`. + if (src_aspect > dst_aspect) { + // Source is wider, crop horizontally. + crop_width = int(vp_tex->get_size().height * dst_aspect); + crop_x = (vp_tex->get_size().width - crop_width) / 2; + vp_tex->crop_from_point(crop_x, crop_y, crop_width, crop_height); + } else if (src_aspect < dst_aspect) { + // Source is taller, crop vertically. + crop_height = int(vp_tex->get_size().width / dst_aspect); + crop_y = (vp_tex->get_size().height - crop_height) / 2; + vp_tex->crop_from_point(crop_x, crop_y, crop_width, crop_height); + } + + vp_tex->resize(movie_size.width, movie_size.height, Image::INTERPOLATE_BILINEAR); + } + if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) { vp_tex->convert(Image::FORMAT_RGBA8); vp_tex->linear_to_srgb(); diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h index 29d5a82c621c..760fcd7a8a91 100644 --- a/servers/movie_writer/movie_writer.h +++ b/servers/movie_writer/movie_writer.h @@ -41,6 +41,10 @@ class MovieWriter : public Object { uint64_t mix_rate = 0; uint32_t audio_channels = 0; + // The output resolution, which can differ from the window size. + // Used as a base for resizing all subsequent frames if their resolution differs. + Vector2i movie_size; + float cpu_time = 0.0f; float gpu_time = 0.0f; uint64_t encoding_time_usec = 0; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index e7b819737a47..a609b6500f18 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -884,10 +884,10 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { start_pos += l_gl[start_pos].count; } - while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos <= end_pos) && (end_pos > 0) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { end_pos -= l_gl[end_pos].count; } - if (last_end <= l_gl[start_pos].start) { + if (last_end <= l_gl[start_pos].start && l_gl[start_pos].start != l_gl[end_pos].end) { lines.push_back(l_gl[start_pos].start); lines.push_back(l_gl[end_pos].end); cur_safe_brk = last_safe_break; @@ -928,10 +928,10 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { start_pos += l_gl[start_pos].count; } - while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos <= end_pos) && (end_pos > 0) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { end_pos -= l_gl[end_pos].count; } - if (last_end <= l_gl[start_pos].start) { + if (last_end <= l_gl[start_pos].start && l_gl[start_pos].start != l_gl[end_pos].end) { lines.push_back(l_gl[start_pos].start); lines.push_back(l_gl[end_pos].end); last_end = l_gl[i].end; @@ -1067,10 +1067,10 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { start_pos += l_gl[start_pos].count; } - while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos <= end_pos) && (end_pos > 0) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { end_pos -= l_gl[end_pos].count; } - if (last_end <= l_gl[start_pos].start) { + if (last_end <= l_gl[start_pos].start && l_gl[start_pos].start != l_gl[end_pos].end) { lines.push_back(l_gl[start_pos].start); lines.push_back(l_gl[end_pos].end); if (p_width > indent && i > indent_end) { @@ -1110,11 +1110,11 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { start_pos += l_gl[start_pos].count; } - while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos <= end_pos) && (end_pos > 0) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { end_pos -= l_gl[end_pos].count; } trim_next = true; - if (last_end <= l_gl[start_pos].start) { + if (last_end <= l_gl[start_pos].start && l_gl[start_pos].start != l_gl[end_pos].end) { lines.push_back(l_gl[start_pos].start); lines.push_back(l_gl[end_pos].end); if (p_width > indent && i > indent_end) { diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index 1cc507213fbe..84c3fe64425e 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -995,6 +995,38 @@ TEST_CASE("[String] sprintf") { REQUIRE(error == false); CHECK(output == String("fish 143 frog")); + // INT64_MIN + format = "fish %d frog"; + args.clear(); + args.push_back(INT64_MIN); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -9223372036854775808 frog")); + + // INT64_MIN hex (lower) + format = "fish %x frog"; + args.clear(); + args.push_back(INT64_MIN); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -8000000000000000 frog")); + + // INT64_MIN hex (upper) + format = "fish %X frog"; + args.clear(); + args.push_back(INT64_MIN); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -8000000000000000 frog")); + + // INT64_MIN octal + format = "fish %o frog"; + args.clear(); + args.push_back(INT64_MIN); + output = format.sprintf(args, &error); + REQUIRE(error == false); + CHECK(output == String("fish -1000000000000000000000 frog")); + ///// Reals // Real diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h index e28c28c3ed55..eb4015aa01bf 100644 --- a/tests/servers/test_text_server.h +++ b/tests/servers/test_text_server.h @@ -603,6 +603,59 @@ TEST_SUITE("[TextServer]") { ts->free_rid(ctx); + String test_2 = U"Word Wrap"; + // 5^ + + ctx = ts->create_shaped_text(); + CHECK_FALSE_MESSAGE(ctx == RID(), "Creating text buffer failed."); + ok = ts->shaped_text_add_string(ctx, test_2, font, 16); + CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed."); + + brks = ts->shaped_text_get_line_breaks(ctx, 43); + CHECK_FALSE_MESSAGE(brks.size() != 4, "Invalid line breaks number."); + if (brks.size() == 4) { + CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[1] != 5, "Invalid line break position."); + + CHECK_FALSE_MESSAGE(brks[2] != 5, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[3] != 9, "Invalid line break position."); + } + + brks = ts->shaped_text_get_line_breaks(ctx, 43.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES); + CHECK_FALSE_MESSAGE(brks.size() != 4, "Invalid line breaks number."); + if (brks.size() == 4) { + CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[1] != 4, "Invalid line break position."); + + CHECK_FALSE_MESSAGE(brks[2] != 5, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[3] != 9, "Invalid line break position."); + } + + brks = ts->shaped_text_get_line_breaks(ctx, 43.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES); + CHECK_FALSE_MESSAGE(brks.size() != 4, "Invalid line breaks number."); + if (brks.size() == 4) { + CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[1] != 4, "Invalid line break position."); + + CHECK_FALSE_MESSAGE(brks[2] != 5, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[3] != 9, "Invalid line break position."); + } + + brks = ts->shaped_text_get_line_breaks(ctx, 43.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY); + CHECK_FALSE_MESSAGE(brks.size() != 6, "Invalid line breaks number."); + if (brks.size() == 6) { + CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[1] != 4, "Invalid line break position."); + + CHECK_FALSE_MESSAGE(brks[2] != 4, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[3] != 5, "Invalid line break position."); + + CHECK_FALSE_MESSAGE(brks[4] != 5, "Invalid line break position."); + CHECK_FALSE_MESSAGE(brks[5] != 9, "Invalid line break position."); + } + + ts->free_rid(ctx); + for (int j = 0; j < font.size(); j++) { ts->free_rid(font[j]); } diff --git a/thirdparty/README.md b/thirdparty/README.md index 947b9f552d64..0eea516ea9b8 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -579,7 +579,7 @@ Files extracted from upstream source: ## libpng - Upstream: http://libpng.org/pub/png/libpng.html -- Version: 1.6.48 (ea127968204cc5d10f3fc9250c306b9e8cbd9b80, 2025) +- Version: 1.6.53 (4e3f57d50f552841550a36eabbb3fbcecacb7750, 2025) - License: libpng/zlib Files extracted from upstream source: @@ -654,7 +654,7 @@ File extracted from upstream source: ## mbedtls - Upstream: https://github.com/Mbed-TLS/mbedtls -- Version: 3.6.4 (c765c831e5c2a0971410692f92f7a81d6ec65ec2, 2025) +- Version: 3.6.5 (e185d7fd85499c8ce5ca2a54f5cf8fe7dbe3f8df, 2025) - License: Apache 2.0 File extracted from upstream release tarball: @@ -664,7 +664,7 @@ File extracted from upstream release tarball: - From `library/` to `thirdparty/mbedtls/library/`: - All `.c` and `.h` files - Except `bignum_mod.c`, `block_cipher.c`, `ecp_curves_new.c`, `lmots.c`, - `lms.c`, `bignum_core_invasive.h` + `lms.c` - The `LICENSE` file (edited to keep only the Apache 2.0 variant) - Added 2 files `godot_core_mbedtls_platform.c` and `godot_core_mbedtls_config.h` providing configuration for light bundling with core diff --git a/thirdparty/libpng/png.c b/thirdparty/libpng/png.c index a9179ffbbddc..85b49496520b 100644 --- a/thirdparty/libpng/png.c +++ b/thirdparty/libpng/png.c @@ -13,7 +13,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_48 Your_png_h_is_not_version_1_6_48; +typedef png_libpng_version_1_6_53 Your_png_h_is_not_version_1_6_53; /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the * corresponding macro definitions. This causes a compile time failure if @@ -108,10 +108,16 @@ png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) if (png_ptr == NULL) return NULL; - if (items >= (~(png_alloc_size_t)0)/size) + /* This check against overflow is vestigial, dating back from + * the old times when png_zalloc used to be an exported function. + * We're still keeping it here for now, as an extra-cautious + * prevention against programming errors inside zlib, although it + * should rather be a debug-time assertion instead. + */ + if (size != 0 && items >= (~(png_alloc_size_t)0) / size) { - png_warning (png_voidcast(png_structrp, png_ptr), - "Potential overflow in png_zalloc()"); + png_warning(png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); return NULL; } @@ -238,10 +244,6 @@ png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) png_warning(png_ptr, m); #endif -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags = 0; -#endif - return 0; } @@ -815,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.48" PNG_STRING_NEWLINE \ + "libpng version 1.6.53" PNG_STRING_NEWLINE \ "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ diff --git a/thirdparty/libpng/png.h b/thirdparty/libpng/png.h index 665511c5547d..bdcd243dea20 100644 --- a/thirdparty/libpng/png.h +++ b/thirdparty/libpng/png.h @@ -1,6 +1,6 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.48 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson @@ -14,7 +14,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.48, April 2025: + * libpng versions 1.6.36, December 2018, through 1.6.53, December 2025: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -238,7 +238,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.48 16 10648 16.so.16.48[.0] + * 1.6.53 16 10651 16.so.16.53[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -274,7 +274,7 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.48" +#define PNG_LIBPNG_VER_STRING "1.6.53" #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n" /* The versions of shared library builds should stay in sync, going forward */ @@ -285,7 +285,7 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 48 +#define PNG_LIBPNG_VER_RELEASE 53 /* This should be zero for a public release, or non-zero for a * development version. @@ -316,7 +316,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10648 /* 1.6.48 */ +#define PNG_LIBPNG_VER 10653 /* 1.6.53 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -426,7 +426,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_48; +typedef char* png_libpng_version_1_6_53; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * @@ -3303,26 +3303,45 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, * selected at run time. */ #ifdef PNG_SET_OPTION_SUPPORTED + +/* HARDWARE: ARM Neon SIMD instructions supported */ #ifdef PNG_ARM_NEON_API_SUPPORTED -# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +# define PNG_ARM_NEON 0 #endif -#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ -#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ + +/* SOFTWARE: Force maximum window */ +#define PNG_MAXIMUM_INFLATE_WINDOW 2 + +/* SOFTWARE: Check ICC profile for sRGB */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 + +/* HARDWARE: MIPS MSA SIMD instructions supported */ #ifdef PNG_MIPS_MSA_API_SUPPORTED -# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +# define PNG_MIPS_MSA 6 #endif + +/* SOFTWARE: Disable Adler32 check on IDAT */ #ifdef PNG_DISABLE_ADLER32_CHECK_SUPPORTED -# define PNG_IGNORE_ADLER32 8 /* SOFTWARE: disable Adler32 check on IDAT */ +# define PNG_IGNORE_ADLER32 8 #endif + +/* HARDWARE: PowerPC VSX SIMD instructions supported */ #ifdef PNG_POWERPC_VSX_API_SUPPORTED -# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions - * supported */ +# define PNG_POWERPC_VSX 10 #endif + +/* HARDWARE: MIPS MMI SIMD instructions supported */ #ifdef PNG_MIPS_MMI_API_SUPPORTED -# define PNG_MIPS_MMI 12 /* HARDWARE: MIPS MMI SIMD instructions supported */ +# define PNG_MIPS_MMI 12 +#endif + +/* HARDWARE: RISC-V RVV SIMD instructions supported */ +#ifdef PNG_RISCV_RVV_API_SUPPORTED +# define PNG_RISCV_RVV 14 #endif -#define PNG_OPTION_NEXT 14 /* Next option - numbers must be even */ +/* Next option - numbers must be even */ +#define PNG_OPTION_NEXT 16 /* Return values: NOTE: there are four values and 'off' is *not* zero */ #define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ diff --git a/thirdparty/libpng/pngconf.h b/thirdparty/libpng/pngconf.h index e46b06111904..f4ff19209c67 100644 --- a/thirdparty/libpng/pngconf.h +++ b/thirdparty/libpng/pngconf.h @@ -1,6 +1,6 @@ /* pngconf.h - machine-configurable file for libpng * - * libpng version 1.6.48 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson diff --git a/thirdparty/libpng/pngdebug.h b/thirdparty/libpng/pngdebug.h index af1ae9e82127..0337918aec37 100644 --- a/thirdparty/libpng/pngdebug.h +++ b/thirdparty/libpng/pngdebug.h @@ -38,9 +38,6 @@ #define PNGDEBUG_H /* These settings control the formatting of messages in png.c and pngerror.c */ /* Moved to pngdebug.h at 1.5.0 */ -# ifndef PNG_LITERAL_SHARP -# define PNG_LITERAL_SHARP 0x23 -# endif # ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET # define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b # endif diff --git a/thirdparty/libpng/pngerror.c b/thirdparty/libpng/pngerror.c index 01a7ef5347eb..044fa2eb68c2 100644 --- a/thirdparty/libpng/pngerror.c +++ b/thirdparty/libpng/pngerror.c @@ -39,46 +39,6 @@ PNG_FUNCTION(void,PNGAPI png_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - char msg[16]; - if (png_ptr != NULL) - { - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) - { - if (*error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - for (offset = 1; offset<15; offset++) - if (error_message[offset] == ' ') - break; - - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - int i; - for (i = 0; i < offset - 1; i++) - msg[i] = error_message[i + 1]; - msg[i - 1] = '\0'; - error_message = msg; - } - - else - error_message += offset; - } - - else - { - if ((png_ptr->flags & PNG_FLAG_STRIP_ERROR_TEXT) != 0) - { - msg[0] = '0'; - msg[1] = '\0'; - error_message = msg; - } - } - } - } -#endif if (png_ptr != NULL && png_ptr->error_fn != NULL) (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), error_message); @@ -216,21 +176,6 @@ void PNGAPI png_warning(png_const_structrp png_ptr, png_const_charp warning_message) { int offset = 0; - if (png_ptr != NULL) - { -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - if ((png_ptr->flags & - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) != 0) -#endif - { - if (*warning_message == PNG_LITERAL_SHARP) - { - for (offset = 1; offset < 15; offset++) - if (warning_message[offset] == ' ') - break; - } - } - } if (png_ptr != NULL && png_ptr->warning_fn != NULL) (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), warning_message + offset); @@ -712,42 +657,9 @@ png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), PNG_NORETURN) { #ifdef PNG_CONSOLE_IO_SUPPORTED -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - /* Check on NULL only added in 1.5.4 */ - if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) - { - /* Strip "#nnnn " from beginning of error message. */ - int offset; - char error_number[16]; - for (offset = 0; offset<15; offset++) - { - error_number[offset] = error_message[offset + 1]; - if (error_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - error_number[offset - 1] = '\0'; - fprintf(stderr, "libpng error no. %s: %s", - error_number, error_message + offset + 1); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng error: %s, offset=%d", - error_message, offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -#endif - { - fprintf(stderr, "libpng error: %s", error_message ? error_message : - "undefined"); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(error_message) /* Make compiler happy */ #endif @@ -785,40 +697,8 @@ static void /* PRIVATE */ png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) { #ifdef PNG_CONSOLE_IO_SUPPORTED -# ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*warning_message == PNG_LITERAL_SHARP) - { - int offset; - char warning_number[16]; - for (offset = 0; offset < 15; offset++) - { - warning_number[offset] = warning_message[offset + 1]; - if (warning_message[offset] == ' ') - break; - } - - if ((offset > 1) && (offset < 15)) - { - warning_number[offset + 1] = '\0'; - fprintf(stderr, "libpng warning no. %s: %s", - warning_number, warning_message + offset); - fprintf(stderr, PNG_STRING_NEWLINE); - } - - else - { - fprintf(stderr, "libpng warning: %s", - warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } - } - else -# endif - - { - fprintf(stderr, "libpng warning: %s", warning_message); - fprintf(stderr, PNG_STRING_NEWLINE); - } + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); #else PNG_UNUSED(warning_message) /* Make compiler happy */ #endif @@ -866,12 +746,8 @@ png_get_error_ptr(png_const_structrp png_ptr) void PNGAPI png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) { - if (png_ptr != NULL) - { - png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | - PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); - } + PNG_UNUSED(png_ptr) + PNG_UNUSED(strip_mode) } #endif diff --git a/thirdparty/libpng/pnglibconf.h b/thirdparty/libpng/pnglibconf.h index c7634440de85..27fa87045b3d 100644 --- a/thirdparty/libpng/pnglibconf.h +++ b/thirdparty/libpng/pnglibconf.h @@ -1,6 +1,6 @@ /* pnglibconf.h - library build configuration */ -/* libpng version 1.6.48 */ +/* libpng version 1.6.53 */ /* Copyright (c) 2018-2025 Cosmin Truta */ /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ diff --git a/thirdparty/libpng/pngpread.c b/thirdparty/libpng/pngpread.c index 0a3e822cf47c..37aa432aeca9 100644 --- a/thirdparty/libpng/pngpread.c +++ b/thirdparty/libpng/pngpread.c @@ -229,6 +229,14 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) png_benign_error(png_ptr, "Too many IDATs found"); } + else if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + { + /* These flags must be set consistently for all non-IDAT chunks, + * including the unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT | PNG_AFTER_IDAT; + } + if (chunk_name == png_IHDR) { if (png_ptr->push_length != 13) diff --git a/thirdparty/libpng/pngpriv.h b/thirdparty/libpng/pngpriv.h index a6f8d67d3841..fc8d461cf5f0 100644 --- a/thirdparty/libpng/pngpriv.h +++ b/thirdparty/libpng/pngpriv.h @@ -143,6 +143,20 @@ # endif #endif +#ifndef PNG_RISCV_RVV_OPT + /* RISCV_RVV optimizations are being controlled by the compiler settings, + * typically the target compiler will define __riscv but the rvv extension + * availability has to be explicitly stated. This is why if no + * PNG_RISCV_RVV_OPT was defined then a runtime check will be executed. + * + * To enable RISCV_RVV optimizations unconditionally, and compile the + * associated code, pass --enable-riscv-rvv=yes or --enable-riscv-rvv=on + * to configure or put -DPNG_RISCV_RVV_OPT=2 in CPPFLAGS. + */ + +# define PNG_RISCV_RVV_OPT 0 +#endif + #if PNG_ARM_NEON_OPT > 0 /* NEON optimizations are to be at least considered by libpng, so enable the * callbacks to do this. @@ -288,6 +302,16 @@ # define PNG_LOONGARCH_LSX_IMPLEMENTATION 0 #endif +#if PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_rvv +# ifndef PNG_RISCV_RVV_IMPLEMENTATION + /* Use the intrinsics code by default. */ +# define PNG_RISCV_RVV_IMPLEMENTATION 1 +# endif +#else +# define PNG_RISCV_RVV_IMPLEMENTATION 0 +#endif /* PNG_RISCV_RVV_OPT > 0 && __riscv_v >= 1000000 */ + /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If * so PNG_BUILD_DLL must be set. @@ -686,7 +710,7 @@ /* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000U */ /* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000U */ #define PNG_FLAG_LIBRARY_MISMATCH 0x20000U -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000U + /* 0x40000U unused */ #define PNG_FLAG_STRIP_ERROR_TEXT 0x80000U #define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000U /* Added to libpng-1.4.0 */ #define PNG_FLAG_APP_WARNINGS_WARN 0x200000U /* Added to libpng-1.6.0 */ @@ -1522,6 +1546,23 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_lsx,(png_row_infop row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); #endif +#if PNG_RISCV_RVV_IMPLEMENTATION == 1 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_rvv,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + /* Choose the best filter to use and filter the row data */ PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); @@ -2134,6 +2175,11 @@ PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_lsx, (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); #endif +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 +PNG_INTERNAL_FUNCTION(void, png_init_filter_functions_rvv, + (png_structp png_ptr, unsigned int bpp), PNG_EMPTY); +#endif + PNG_INTERNAL_FUNCTION(png_uint_32, png_check_keyword, (png_structrp png_ptr, png_const_charp key, png_bytep new_key), PNG_EMPTY); diff --git a/thirdparty/libpng/pngread.c b/thirdparty/libpng/pngread.c index 4f55f6408b8e..13a93deedcba 100644 --- a/thirdparty/libpng/pngread.c +++ b/thirdparty/libpng/pngread.c @@ -702,7 +702,12 @@ png_read_end(png_structrp png_ptr, png_inforp info_ptr) png_uint_32 chunk_name = png_ptr->chunk_name; if (chunk_name != png_IDAT) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + { + /* These flags must be set consistently for all non-IDAT chunks, + * including the unknown chunks. + */ + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT | PNG_AFTER_IDAT; + } if (chunk_name == png_IEND) png_handle_chunk(png_ptr, info_ptr, length); @@ -809,7 +814,8 @@ png_read_destroy(png_structrp png_ptr) #endif #if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_ARM_NEON_IMPLEMENTATION) + (defined(PNG_ARM_NEON_IMPLEMENTATION) || \ + defined(PNG_RISCV_RVV_IMPLEMENTATION)) png_free(png_ptr, png_ptr->riffled_palette); png_ptr->riffled_palette = NULL; #endif @@ -3123,6 +3129,54 @@ png_image_read_colormapped(png_voidp argument) } } +/* Row reading for interlaced 16-to-8 bit depth conversion with local buffer. */ +static int +png_image_read_direct_scaled(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_bytep local_row = png_voidcast(png_bytep, display->local_row); + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + int passes; + + /* Handle interlacing. */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + png_error(png_ptr, "unknown interlace type"); + } + + /* Read each pass using local_row as intermediate buffer. */ + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep output_row = first_row; + + for (; y > 0; --y) + { + /* Read into local_row (gets transformed 8-bit data). */ + png_read_row(png_ptr, local_row, NULL); + + /* Copy from local_row to user buffer. */ + memcpy(output_row, local_row, (size_t)row_bytes); + output_row += row_bytes; + } + } + + return 1; +} + /* Just the row reading part of png_image_read. */ static int png_image_read_composite(png_voidp argument) @@ -3153,6 +3207,7 @@ png_image_read_composite(png_voidp argument) ptrdiff_t step_row = display->row_bytes; unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) != 0 ? 3 : 1; + int optimize_alpha = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; int pass; for (pass = 0; pass < passes; ++pass) @@ -3209,20 +3264,44 @@ png_image_read_composite(png_voidp argument) if (alpha < 255) /* else just use component */ { - /* This is PNG_OPTIMIZED_ALPHA, the component value - * is a linear 8-bit value. Combine this with the - * current outrow[c] value which is sRGB encoded. - * Arithmetic here is 16-bits to preserve the output - * values correctly. - */ - component *= 257*255; /* =65535 */ - component += (255-alpha)*png_sRGB_table[outrow[c]]; + if (optimize_alpha != 0) + { + /* This is PNG_OPTIMIZED_ALPHA, the component value + * is a linear 8-bit value. Combine this with the + * current outrow[c] value which is sRGB encoded. + * Arithmetic here is 16-bits to preserve the output + * values correctly. + */ + component *= 257*255; /* =65535 */ + component += (255-alpha)*png_sRGB_table[outrow[c]]; - /* So 'component' is scaled by 255*65535 and is - * therefore appropriate for the sRGB to linear - * conversion table. - */ - component = PNG_sRGB_FROM_LINEAR(component); + /* Clamp to the valid range to defend against + * unforeseen cases where the data might be sRGB + * instead of linear premultiplied. + * (Belt-and-suspenders for CVE-2025-66293.) + */ + if (component > 255*65535) + component = 255*65535; + + /* So 'component' is scaled by 255*65535 and is + * therefore appropriate for the sRGB-to-linear + * conversion table. + */ + component = PNG_sRGB_FROM_LINEAR(component); + } + else + { + /* Compositing was already done on the palette + * entries. The data is sRGB premultiplied on black. + * Composite with the background in sRGB space. + * This is not gamma-correct, but matches what was + * done to the palette. + */ + png_uint_32 background = outrow[c]; + component += ((255-alpha) * background + 127) / 255; + if (component > 255) + component = 255; + } } outrow[c] = (png_byte)component; @@ -3541,6 +3620,7 @@ png_image_read_direct(png_voidp argument) int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; int do_local_compose = 0; int do_local_background = 0; /* to avoid double gamma correction bug */ + int do_local_scale = 0; /* for interlaced 16-to-8 bit conversion */ int passes = 0; /* Add transforms to ensure the correct output format is produced then check @@ -3674,8 +3754,16 @@ png_image_read_direct(png_voidp argument) png_set_expand_16(png_ptr); else /* 8-bit output */ + { png_set_scale_16(png_ptr); + /* For interlaced images, use local_row buffer to avoid overflow + * in png_combine_row() which writes using IHDR bit-depth. + */ + if (png_ptr->interlaced != 0) + do_local_scale = 1; + } + change &= ~PNG_FORMAT_FLAG_LINEAR; } @@ -3951,6 +4039,24 @@ png_image_read_direct(png_voidp argument) return result; } + else if (do_local_scale != 0) + { + /* For interlaced 16-to-8 conversion, use an intermediate row buffer + * to avoid buffer overflows in png_combine_row. The local_row is sized + * for the transformed (8-bit) output, preventing the overflow that would + * occur if png_combine_row wrote 16-bit data directly to the user buffer. + */ + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_direct_scaled, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + else { png_alloc_size_t row_bytes = (png_alloc_size_t)display->row_bytes; diff --git a/thirdparty/libpng/pngrtran.c b/thirdparty/libpng/pngrtran.c index 0fea13a418d8..507d11381ec0 100644 --- a/thirdparty/libpng/pngrtran.c +++ b/thirdparty/libpng/pngrtran.c @@ -28,6 +28,12 @@ # endif #endif +#ifdef PNG_RISCV_RVV_IMPLEMENTATION +# if PNG_RISCV_RVV_IMPLEMENTATION == 1 +# define PNG_RISCV_RVV_INTRINSICS_AVAILABLE +# endif +#endif + #ifdef PNG_READ_SUPPORTED /* Set the action on getting a CRC error for an ancillary or critical chunk. */ @@ -495,9 +501,19 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, { int i; + /* Initialize the array to index colors. + * + * Ensure quantize_index can fit 256 elements (PNG_MAX_PALETTE_LENGTH) + * rather than num_palette elements. This is to prevent buffer overflows + * caused by malformed PNG files with out-of-range palette indices. + * + * Be careful to avoid leaking memory. Applications are allowed to call + * this function more than once per png_struct. + */ + png_free(png_ptr, png_ptr->quantize_index); png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, - (png_alloc_size_t)num_palette); - for (i = 0; i < num_palette; i++) + PNG_MAX_PALETTE_LENGTH); + for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++) png_ptr->quantize_index[i] = (png_byte)i; } @@ -509,15 +525,14 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, * Perhaps not the best solution, but good enough. */ - int i; + png_bytep quantize_sort; + int i, j; - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + /* Initialize the local array to sort colors. */ + quantize_sort = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_palette); - - /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = (png_byte)i; + quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a * bubble sort, and running it until we have sorted @@ -529,19 +544,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = num_palette - 1; i >= maximum_colors; i--) { int done; /* To stop early if the list is pre-sorted */ - int j; done = 1; for (j = 0; j < i; j++) { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) + if (histogram[quantize_sort[j]] + < histogram[quantize_sort[j + 1]]) { png_byte t; - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; + t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j + 1]; + quantize_sort[j + 1] = t; done = 0; } } @@ -553,18 +567,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, /* Swap the palette around, and set up a table, if necessary */ if (full_quantize != 0) { - int j = num_palette; + j = num_palette; /* Put all the useful colors within the max, but don't * move the others. */ for (i = 0; i < maximum_colors; i++) { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); palette[i] = palette[j]; } @@ -572,7 +586,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } else { - int j = num_palette; + j = num_palette; /* Move all the used colors inside the max limit, and * develop a translation table. @@ -580,13 +594,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = 0; i < maximum_colors; i++) { /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; @@ -624,8 +638,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } } } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; + png_free(png_ptr, quantize_sort); } else { @@ -1768,19 +1781,51 @@ png_init_read_transformations(png_structrp png_ptr) } else /* if (png_ptr->trans_alpha[i] != 0xff) */ { - png_byte v, w; - - v = png_ptr->gamma_to_1[palette[i].red]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); - palette[i].red = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[palette[i].green]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); - palette[i].green = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[w]; + if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0) + { + /* Premultiply only: + * component = round((component * alpha) / 255) + */ + png_uint_32 component; + + component = png_ptr->gamma_to_1[palette[i].red]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].red = png_ptr->gamma_from_1[component]; + + component = png_ptr->gamma_to_1[palette[i].green]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].green = png_ptr->gamma_from_1[component]; + + component = png_ptr->gamma_to_1[palette[i].blue]; + component = + (component * png_ptr->trans_alpha[i] + 128) / 255; + palette[i].blue = png_ptr->gamma_from_1[component]; + } + else + { + /* Composite with background color: + * component = + * alpha * component + (1 - alpha) * background + */ + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, + png_ptr->trans_alpha[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } } } else @@ -1798,6 +1843,7 @@ png_init_read_transformations(png_structrp png_ptr) * transformations elsewhere. */ png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; } /* color_type == PNG_COLOR_TYPE_PALETTE */ /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ @@ -5003,13 +5049,8 @@ png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) #ifdef PNG_READ_QUANTIZE_SUPPORTED if ((png_ptr->transformations & PNG_QUANTIZE) != 0) - { png_do_quantize(row_info, png_ptr->row_buf + 1, png_ptr->palette_lookup, png_ptr->quantize_index); - - if (row_info->rowbytes == 0) - png_error(png_ptr, "png_do_quantize returned rowbytes=0"); - } #endif /* READ_QUANTIZE */ #ifdef PNG_READ_EXPAND_16_SUPPORTED diff --git a/thirdparty/libpng/pngrutil.c b/thirdparty/libpng/pngrutil.c index 7d9a00186dc7..e7c7bbe48e9b 100644 --- a/thirdparty/libpng/pngrutil.c +++ b/thirdparty/libpng/pngrutil.c @@ -2412,10 +2412,6 @@ png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #endif - /* TODO: this doesn't work and shouldn't be necessary. */ - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) @@ -2486,10 +2482,6 @@ png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #endif - /* TODO: should not be necessary. */ - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - /* Note, "length" is sufficient here; we won't be adding * a null terminator later. The limit check in png_handle_chunk should be * sufficient. @@ -2606,10 +2598,6 @@ png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #endif - /* TODO: should not be necessary. */ - if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) - png_ptr->mode |= PNG_AFTER_IDAT; - buffer = png_read_buffer(png_ptr, length+1); if (buffer == NULL) diff --git a/thirdparty/libpng/pngstruct.h b/thirdparty/libpng/pngstruct.h index b17a54fe8f99..fe5fa0415548 100644 --- a/thirdparty/libpng/pngstruct.h +++ b/thirdparty/libpng/pngstruct.h @@ -375,7 +375,8 @@ struct png_struct_def /* New member added in libpng-1.6.36 */ #if defined(PNG_READ_EXPAND_SUPPORTED) && \ - defined(PNG_ARM_NEON_IMPLEMENTATION) + (defined(PNG_ARM_NEON_IMPLEMENTATION) || \ + defined(PNG_RISCV_RVV_IMPLEMENTATION)) png_bytep riffled_palette; /* buffer for accelerated palette expansion */ #endif @@ -404,7 +405,6 @@ struct png_struct_def #ifdef PNG_READ_QUANTIZE_SUPPORTED /* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep quantize_sort; /* working sort array */ png_bytep index_to_palette; /* where the original index currently is in the palette */ png_bytep palette_to_index; /* which original index points to this diff --git a/thirdparty/libpng/pngwrite.c b/thirdparty/libpng/pngwrite.c index 35a5d17b601b..83148960effd 100644 --- a/thirdparty/libpng/pngwrite.c +++ b/thirdparty/libpng/pngwrite.c @@ -2173,8 +2173,7 @@ png_image_write_main(png_voidp argument) * before it is written. This only applies when the input is 16-bit and * either there is an alpha channel or it is converted to 8-bit. */ - if ((linear != 0 && alpha != 0 ) || - (colormap == 0 && display->convert_to_8bit != 0)) + if (linear != 0 && (alpha != 0 || display->convert_to_8bit != 0)) { png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr))); diff --git a/thirdparty/mbedtls/include/mbedtls/bignum.h b/thirdparty/mbedtls/include/mbedtls/bignum.h index 1e1c06330fc3..6187856713dc 100644 --- a/thirdparty/mbedtls/include/mbedtls/bignum.h +++ b/thirdparty/mbedtls/include/mbedtls/bignum.h @@ -974,6 +974,7 @@ int mbedtls_mpi_random(mbedtls_mpi *X, * \brief Compute the greatest common divisor: G = gcd(A, B) * * \param G The destination MPI. This must point to an initialized MPI. + * This will always be positive or 0. * \param A The first operand. This must point to an initialized MPI. * \param B The second operand. This must point to an initialized MPI. * @@ -988,10 +989,12 @@ int mbedtls_mpi_gcd(mbedtls_mpi *G, const mbedtls_mpi *A, * \brief Compute the modular inverse: X = A^-1 mod N * * \param X The destination MPI. This must point to an initialized MPI. + * The value returned on success will be between [1, N-1]. * \param A The MPI to calculate the modular inverse of. This must point - * to an initialized MPI. + * to an initialized MPI. This value can be negative, in which + * case a positive answer will still be returned in \p X. * \param N The base of the modular inversion. This must point to an - * initialized MPI. + * initialized MPI and be greater than one. * * \return \c 0 if successful. * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. diff --git a/thirdparty/mbedtls/include/mbedtls/build_info.h b/thirdparty/mbedtls/include/mbedtls/build_info.h index e18e823c15e4..3d867aa1b1c2 100644 --- a/thirdparty/mbedtls/include/mbedtls/build_info.h +++ b/thirdparty/mbedtls/include/mbedtls/build_info.h @@ -26,16 +26,16 @@ */ #define MBEDTLS_VERSION_MAJOR 3 #define MBEDTLS_VERSION_MINOR 6 -#define MBEDTLS_VERSION_PATCH 4 +#define MBEDTLS_VERSION_PATCH 5 /** * The single version number has the following structure: * MMNNPP00 * Major version | Minor version | Patch version */ -#define MBEDTLS_VERSION_NUMBER 0x03060400 -#define MBEDTLS_VERSION_STRING "3.6.4" -#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 3.6.4" +#define MBEDTLS_VERSION_NUMBER 0x03060500 +#define MBEDTLS_VERSION_STRING "3.6.5" +#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 3.6.5" /* Macros for build-time platform detection */ diff --git a/thirdparty/mbedtls/include/mbedtls/cipher.h b/thirdparty/mbedtls/include/mbedtls/cipher.h index 1dc31c9c2419..e5471521556a 100644 --- a/thirdparty/mbedtls/include/mbedtls/cipher.h +++ b/thirdparty/mbedtls/include/mbedtls/cipher.h @@ -329,8 +329,15 @@ typedef struct mbedtls_cipher_context_t { /** Padding functions to use, if relevant for * the specific cipher mode. */ - void(*MBEDTLS_PRIVATE(add_padding))(unsigned char *output, size_t olen, size_t data_len); - int(*MBEDTLS_PRIVATE(get_padding))(unsigned char *input, size_t ilen, size_t *data_len); + void(*MBEDTLS_PRIVATE(add_padding))(unsigned char *output, size_t olen, + size_t data_len); + /* Report invalid-padding condition through the output parameter + * invalid_padding. To minimize changes in Mbed TLS 3.6, where this + * declaration is in a public header, use the public type size_t + * rather than the internal type mbedtls_ct_condition_t. */ + int(*MBEDTLS_PRIVATE(get_padding))(unsigned char *input, size_t ilen, + size_t *data_len, + size_t *invalid_padding); #endif /** Buffer for input that has not been processed yet. */ @@ -878,23 +885,24 @@ int mbedtls_cipher_set_iv(mbedtls_cipher_context_t *ctx, * * \note With non-AEAD ciphers, the order of calls for each message * is as follows: - * 1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce. - * 2. mbedtls_cipher_reset() - * 3. mbedtls_cipher_update() one or more times - * 4. mbedtls_cipher_finish() + * 1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce; + * 2. mbedtls_cipher_reset(); + * 3. mbedtls_cipher_update() zero, one or more times; + * 4. mbedtls_cipher_finish_padded() (recommended for decryption + * if the mode uses padding) or mbedtls_cipher_finish(). * . * This sequence can be repeated to encrypt or decrypt multiple * messages with the same key. * * \note With AEAD ciphers, the order of calls for each message * is as follows: - * 1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce. - * 2. mbedtls_cipher_reset() - * 3. mbedtls_cipher_update_ad() - * 4. mbedtls_cipher_update() one or more times - * 5. mbedtls_cipher_finish() + * 1. mbedtls_cipher_set_iv() if the mode uses an IV/nonce; + * 2. mbedtls_cipher_reset(); + * 3. mbedtls_cipher_update_ad(); + * 4. mbedtls_cipher_update() zero, one or more times; + * 5. mbedtls_cipher_finish() (or mbedtls_cipher_finish_padded()); * 6. mbedtls_cipher_check_tag() (for decryption) or - * mbedtls_cipher_write_tag() (for encryption). + * mbedtls_cipher_write_tag() (for encryption). * . * This sequence can be repeated to encrypt or decrypt multiple * messages with the same key. @@ -930,7 +938,8 @@ int mbedtls_cipher_update_ad(mbedtls_cipher_context_t *ctx, * many block-sized blocks of data as possible to output. * Any data that cannot be written immediately is either * added to the next block, or flushed when - * mbedtls_cipher_finish() is called. + * mbedtls_cipher_finish() or mbedtls_cipher_finish_padded() + * is called. * Exception: For MBEDTLS_MODE_ECB, expects a single block * in size. For example, 16 Bytes for AES. * @@ -964,12 +973,30 @@ int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx, * contained in it is padded to the size of * the last block, and written to the \p output buffer. * + * \warning This function reports invalid padding through an error + * code. Adversaries may be able to decrypt encrypted + * data if they can submit chosen ciphertexts and + * detect whether it has valid padding or not, + * either through direct observation or through a side + * channel such as timing. This is known as a + * padding oracle attack. + * Therefore applications that call this function for + * decryption with a cipher that involves padding + * should take care around error handling. Preferably, + * such applications should use + * mbedtls_cipher_finish_padded() instead of this function. + * * \param ctx The generic cipher context. This must be initialized and * bound to a key. * \param output The buffer to write data to. This needs to be a writable * buffer of at least block_size Bytes. * \param olen The length of the data written to the \p output buffer. * This may not be \c NULL. + * Note that when decrypting in a mode with padding, + * the actual output length is sensitive and may be + * used to mount a padding oracle attack (see warning + * above), although less efficiently than through + * the invalid-padding condition. * * \return \c 0 on success. * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on @@ -977,17 +1004,66 @@ int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx, * \return #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED on decryption * expecting a full block but not receiving one. * \return #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding - * while decrypting. + * while decrypting. Note that invalid-padding errors + * should be handled carefully; see the warning above. * \return A cipher-specific error code on failure. */ int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, unsigned char *output, size_t *olen); +/** + * \brief The generic cipher finalization function. If data still + * needs to be flushed from an incomplete block, the data + * contained in it is padded to the size of + * the last block, and written to the \p output buffer. + * + * \note This function is similar to mbedtls_cipher_finish(). + * The only difference is that it reports invalid padding + * decryption differently, through the \p invalid_padding + * parameter rather than an error code. + * For encryption, and in modes without padding (including + * all authenticated modes), this function is identical + * to mbedtls_cipher_finish(). + * + * \param[in,out] ctx The generic cipher context. This must be initialized and + * bound to a key. + * \param[out] output The buffer to write data to. This needs to be a writable + * buffer of at least block_size Bytes. + * \param[out] olen The length of the data written to the \p output buffer. + * This may not be \c NULL. + * Note that when decrypting in a mode with padding, + * the actual output length is sensitive and may be + * used to mount a padding oracle attack (see warning + * on mbedtls_cipher_finish()). + * \param[out] invalid_padding + * If this function returns \c 0 on decryption, + * \p *invalid_padding is \c 0 if the ciphertext was + * valid, and all-bits-one if the ciphertext had invalid + * padding. + * On encryption, or in a mode without padding (including + * all authenticated modes), \p *invalid_padding is \c 0 + * on success. + * The value in \p *invalid_padding is unspecified if + * this function returns a nonzero status. + * + * \return \c 0 on success. + * Also \c 0 for decryption with invalid padding. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return #MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED on decryption + * expecting a full block but not receiving one. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_finish_padded(mbedtls_cipher_context_t *ctx, + unsigned char *output, size_t *olen, + size_t *invalid_padding); + #if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) /** * \brief This function writes a tag for AEAD ciphers. * Currently supported with GCM and ChaCha20+Poly1305. - * This must be called after mbedtls_cipher_finish(). + * This must be called after mbedtls_cipher_finish() + * or mbedtls_cipher_finish_padded(). * * \param ctx The generic cipher context. This must be initialized, * bound to a key, and have just completed a cipher @@ -1006,7 +1082,8 @@ int mbedtls_cipher_write_tag(mbedtls_cipher_context_t *ctx, /** * \brief This function checks the tag for AEAD ciphers. * Currently supported with GCM and ChaCha20+Poly1305. - * This must be called after mbedtls_cipher_finish(). + * This must be called after mbedtls_cipher_finish() + * or mbedtls_cipher_finish_padded(). * * \param ctx The generic cipher context. This must be initialized. * \param tag The buffer holding the tag. This must be a readable diff --git a/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h b/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h index d2831367cd92..75eff2d89a3b 100644 --- a/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h +++ b/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h @@ -2150,7 +2150,19 @@ /** * \def MBEDTLS_THREADING_ALT * - * Provide your own alternate threading implementation. + * Provide your own alternate implementation of threading primitives + * for mutexes. If you enable this option: + * + * - Provide a header file `"threading_alt.h"`, defining the + * type `mbedtls_threading_mutex_t` of mutex objects. + * + * - Call the function mbedtls_threading_set_alt() in your application + * before calling any other library function (in particular before + * calling psa_crypto_init(), performing an asymmetric cryptography + * operation, or starting a TLS connection). + * + * See mbedtls/threading.h for more details, especially the documentation + * of mbedtls_threading_set_alt(). * * Requires: MBEDTLS_THREADING_C * diff --git a/thirdparty/mbedtls/include/mbedtls/threading.h b/thirdparty/mbedtls/include/mbedtls/threading.h index b4df0e38bebf..7b55a4649677 100644 --- a/thirdparty/mbedtls/include/mbedtls/threading.h +++ b/thirdparty/mbedtls/include/mbedtls/threading.h @@ -51,15 +51,45 @@ typedef struct mbedtls_threading_mutex_t { * mbedtls_threading_free_alt() must be called once in the main * thread after all other Mbed TLS functions. * - * \note mutex_init() and mutex_free() don't return a status code. - * If mutex_init() fails, it should leave its argument (the - * mutex) in a state such that mutex_lock() will fail when - * called with this argument. + * \warning \p mutex_init and \p mutex_free don't return a status code. + * If \p mutex_init fails, it should leave the mutex in + * a state such that \p mutex_lock will reliably return + * #MBEDTLS_ERR_THREADING_MUTEX_ERROR called on this mutex, + * and \p mutex_free will do nothing. * - * \param mutex_init the init function implementation - * \param mutex_free the free function implementation - * \param mutex_lock the lock function implementation - * \param mutex_unlock the unlock function implementation + * \param mutex_init The init function implementation.
+ * The behavior is undefined if the mutex is already + * initialized and has not been destroyed. + * On platforms where mutex initialization can fail, + * since this function does not return a status code, + * it must leave the mutex object in a safe state where + * subsequent function calls will not cause undefined + * behavior: after a call to \p mutex_init, the + * function \p mutex_lock must either succeed or + * fail with a nonzero status code, and the function + * \p mutex_free must free any resources associated + * with the mutex.. + * \param mutex_free The destroy function implementation.
+ * This function must free any resources associated + * with the mutex object.
+ * This function must work reliably if \p mutex_init + * has been called on the mutex and \p mutex_free + * has not yet been called.
+ * The behavior is undefined if the mutex was not + * initialized, if it has already been destroyed, + * if it is currently locked, or if this function + * is called concurrently from multiple threads. + * \param mutex_lock The lock function implementation.
+ * This function must work reliably on any mutex + * which is not currently locked and on which + * \p mutex_init has already been called but + * \p mutex_free has not been called yet.
+ * The behavior is undefined if the mutex was not + * initialized, if it has already been destroyed, or if + * it is currently locked by the calling thread. + * \param mutex_unlock The unlock function implementation.
+ * The behavior is undefined if the mutex is not + * currently locked by the calling thread. */ void mbedtls_threading_set_alt(void (*mutex_init)(mbedtls_threading_mutex_t *), void (*mutex_free)(mbedtls_threading_mutex_t *), diff --git a/thirdparty/mbedtls/include/psa/crypto_extra.h b/thirdparty/mbedtls/include/psa/crypto_extra.h index e503c9e3ca5a..7a9811bb652f 100644 --- a/thirdparty/mbedtls/include/psa/crypto_extra.h +++ b/thirdparty/mbedtls/include/psa/crypto_extra.h @@ -600,9 +600,10 @@ psa_status_t mbedtls_psa_platform_get_builtin_key( * This means that PSA core was built with the corresponding PSA_WANT_ALG_xxx * set and that psa_crypto_init has already been called. * - * \note When using Mbed TLS version of PSA core (i.e. MBEDTLS_PSA_CRYPTO_C is - * set) for now this function only checks the state of the driver - * subsystem, not the algorithm. This might be improved in the future. + * \note When using the built-in version of the PSA core (i.e. + * #MBEDTLS_PSA_CRYPTO_C is set), for now, this function only checks + * the state of the driver subsystem, not the algorithm. + * This might be improved in the future. * * \param hash_alg The hash algorithm. * @@ -610,6 +611,21 @@ psa_status_t mbedtls_psa_platform_get_builtin_key( */ int psa_can_do_hash(psa_algorithm_t hash_alg); +/** + * Tell if PSA is ready for this cipher. + * + * \note When using the built-in version of the PSA core (i.e. + * #MBEDTLS_PSA_CRYPTO_C is set), for now, this function only checks + * the state of the driver subsystem, not the key type and algorithm. + * This might be improved in the future. + * + * \param key_type The key type. + * \param cipher_alg The cipher algorithm. + * + * \return 1 if the PSA can handle \p cipher_alg, 0 otherwise. + */ +int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg); + /**@}*/ /** \addtogroup crypto_types @@ -744,6 +760,17 @@ int psa_can_do_hash(psa_algorithm_t hash_alg); * To make the authentication explicit there are various methods, see Section 5 * of RFC 8236 for two examples. * + * \note The JPAKE implementation has the following limitations: + * - The only supported primitive is ECC on the curve secp256r1, i.e. + * `PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + * PSA_ECC_FAMILY_SECP_R1, 256)`. + * - The only supported hash algorithm is SHA-256, i.e. + * `PSA_ALG_SHA_256`. + * - When using the built-in implementation, the user ID and the peer ID + * must be `"client"` (6-byte string) and `"server"` (6-byte string), + * or the other way round. + * Third-party drivers may or may not have this limitation. + * */ #define PSA_ALG_JPAKE ((psa_algorithm_t) 0x0a000100) @@ -1182,6 +1209,8 @@ static psa_algorithm_t psa_pake_cs_get_algorithm( * This function overwrites any PAKE algorithm * previously set in \p cipher_suite. * + * \note For #PSA_ALG_JPAKE, the only supported hash algorithm is SHA-256. + * * \param[out] cipher_suite The cipher suite structure to write to. * \param algorithm The PAKE algorithm to write. * (`PSA_ALG_XXX` values of type ::psa_algorithm_t @@ -1205,6 +1234,10 @@ static psa_pake_primitive_t psa_pake_cs_get_primitive( * * This function overwrites any primitive previously set in \p cipher_suite. * + * \note For #PSA_ALG_JPAKE, the only supported primitive is ECC on the curve + * secp256r1, i.e. `PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + * PSA_ECC_FAMILY_SECP_R1, 256)`. + * * \param[out] cipher_suite The cipher suite structure to write to. * \param primitive The primitive to write. If this is 0, the * primitive type in \p cipher_suite becomes @@ -1543,6 +1576,10 @@ psa_status_t psa_pake_set_password_key(psa_pake_operation_t *operation, * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) * for more information. * + * \note When using the built-in implementation of #PSA_ALG_JPAKE, the user ID + * must be `"client"` (6-byte string) or `"server"` (6-byte string). + * Third-party drivers may or may not have this limitation. + * * \param[in,out] operation The operation object to set the user ID for. It * must have been set up by psa_pake_setup() and * not yet in use (neither psa_pake_output() nor @@ -1584,6 +1621,10 @@ psa_status_t psa_pake_set_user(psa_pake_operation_t *operation, * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) * for more information. * + * \note When using the built-in implementation of #PSA_ALG_JPAKE, the peer ID + * must be `"client"` (6-byte string) or `"server"` (6-byte string). + * Third-party drivers may or may not have this limitation. + * * \param[in,out] operation The operation object to set the peer ID for. It * must have been set up by psa_pake_setup() and * not yet in use (neither psa_pake_output() nor diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c index 424490951d9e..f6b8f9998121 100644 --- a/thirdparty/mbedtls/library/bignum.c +++ b/thirdparty/mbedtls/library/bignum.c @@ -430,13 +430,6 @@ int mbedtls_mpi_set_bit(mbedtls_mpi *X, size_t pos, unsigned char val) return ret; } -/* - * Return the number of less significant zero-bits - */ -size_t mbedtls_mpi_lsb(const mbedtls_mpi *X) -{ - size_t i; - #if defined(__has_builtin) #if (MBEDTLS_MPI_UINT_MAX == UINT_MAX) && __has_builtin(__builtin_ctz) #define mbedtls_mpi_uint_ctz __builtin_ctz @@ -447,22 +440,34 @@ size_t mbedtls_mpi_lsb(const mbedtls_mpi *X) #endif #endif -#if defined(mbedtls_mpi_uint_ctz) +#if !defined(mbedtls_mpi_uint_ctz) +static size_t mbedtls_mpi_uint_ctz(mbedtls_mpi_uint x) +{ + size_t count = 0; + mbedtls_ct_condition_t done = MBEDTLS_CT_FALSE; + + for (size_t i = 0; i < biL; i++) { + mbedtls_ct_condition_t non_zero = mbedtls_ct_bool((x >> i) & 1); + done = mbedtls_ct_bool_or(done, non_zero); + count = mbedtls_ct_size_if(done, count, i + 1); + } + + return count; +} +#endif + +/* + * Return the number of less significant zero-bits + */ +size_t mbedtls_mpi_lsb(const mbedtls_mpi *X) +{ + size_t i; + for (i = 0; i < X->n; i++) { if (X->p[i] != 0) { return i * biL + mbedtls_mpi_uint_ctz(X->p[i]); } } -#else - size_t count = 0; - for (i = 0; i < X->n; i++) { - for (size_t j = 0; j < biL; j++, count++) { - if (((X->p[i] >> j) & 1) != 0) { - return count; - } - } - } -#endif return 0; } @@ -1743,104 +1748,122 @@ int mbedtls_mpi_exp_mod_unsafe(mbedtls_mpi *X, const mbedtls_mpi *A, return mbedtls_mpi_exp_mod_optionally_safe(X, A, E, MBEDTLS_MPI_IS_PUBLIC, N, prec_RR); } +/* Constant-time GCD and/or modinv with odd modulus and A <= N */ +int mbedtls_mpi_gcd_modinv_odd(mbedtls_mpi *G, + mbedtls_mpi *I, + const mbedtls_mpi *A, + const mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi local_g; + mbedtls_mpi_uint *T = NULL; + const size_t T_factor = I != NULL ? 5 : 4; + const mbedtls_mpi_uint zero = 0; + + /* Check requirements on A and N */ + if (mbedtls_mpi_cmp_int(A, 0) < 0 || + mbedtls_mpi_cmp_mpi(A, N) > 0 || + mbedtls_mpi_get_bit(N, 0) != 1 || + (I != NULL && mbedtls_mpi_cmp_int(N, 1) == 0)) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + /* Check aliasing requirements */ + if (A == N || (I != NULL && (I == N || G == N))) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&local_g); + + if (G == NULL) { + G = &local_g; + } + + /* We can't modify the values of G or I before use in the main function, + * as they could be aliased to A or N. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(G, N->n)); + if (I != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(I, N->n)); + } + + T = mbedtls_calloc(sizeof(mbedtls_mpi_uint) * N->n, T_factor); + if (T == NULL) { + ret = MBEDTLS_ERR_MPI_ALLOC_FAILED; + goto cleanup; + } + + mbedtls_mpi_uint *Ip = I != NULL ? I->p : NULL; + /* If A is 0 (null), then A->p would be null, and A->n would be 0, + * which would be an issue if A->p and A->n were passed to + * mbedtls_mpi_core_gcd_modinv_odd below. */ + const mbedtls_mpi_uint *Ap = A->p != NULL ? A->p : &zero; + size_t An = A->n >= N->n ? N->n : A->p != NULL ? A->n : 1; + mbedtls_mpi_core_gcd_modinv_odd(G->p, Ip, Ap, An, N->p, N->n, T); + + G->s = 1; + if (I != NULL) { + I->s = 1; + } + + if (G->n > N->n) { + memset(G->p + N->n, 0, ciL * (G->n - N->n)); + } + if (I != NULL && I->n > N->n) { + memset(I->p + N->n, 0, ciL * (I->n - N->n)); + } + +cleanup: + mbedtls_mpi_free(&local_g); + mbedtls_free(T); + return ret; +} + /* - * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + * Greatest common divisor: G = gcd(A, B) + * Wrapper around mbedtls_mpi_gcd_modinv() that removes its restrictions. */ int mbedtls_mpi_gcd(mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *B) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - size_t lz, lzt; mbedtls_mpi TA, TB; mbedtls_mpi_init(&TA); mbedtls_mpi_init(&TB); + /* Make copies and take absolute values */ MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TA, A)); MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TB, B)); + TA.s = TB.s = 1; - lz = mbedtls_mpi_lsb(&TA); - lzt = mbedtls_mpi_lsb(&TB); + /* Make the two values the same (non-zero) number of limbs. + * This is needed to use mbedtls_mpi_core functions below. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&TA, TB.n != 0 ? TB.n : 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&TB, TA.n)); // non-zero from above - /* The loop below gives the correct result when A==0 but not when B==0. - * So have a special case for B==0. Leverage the fact that we just - * calculated the lsb and lsb(B)==0 iff B is odd or 0 to make the test - * slightly more efficient than cmp_int(). */ - if (lzt == 0 && mbedtls_mpi_get_bit(&TB, 0) == 0) { - ret = mbedtls_mpi_copy(G, A); + /* Handle special cases (that don't happen in crypto usage) */ + if (mbedtls_mpi_core_check_zero_ct(TA.p, TA.n) == MBEDTLS_CT_FALSE) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(G, &TB)); // GCD(0, B) = abs(B) goto cleanup; } - - if (lzt < lz) { - lz = lzt; + if (mbedtls_mpi_core_check_zero_ct(TB.p, TB.n) == MBEDTLS_CT_FALSE) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(G, &TA)); // GCD(A, 0) = abs(A) + goto cleanup; } - TA.s = TB.s = 1; - - /* We mostly follow the procedure described in HAC 14.54, but with some - * minor differences: - * - Sequences of multiplications or divisions by 2 are grouped into a - * single shift operation. - * - The procedure in HAC assumes that 0 < TB <= TA. - * - The condition TB <= TA is not actually necessary for correctness. - * TA and TB have symmetric roles except for the loop termination - * condition, and the shifts at the beginning of the loop body - * remove any significance from the ordering of TA vs TB before - * the shifts. - * - If TA = 0, the loop goes through 0 iterations and the result is - * correctly TB. - * - The case TB = 0 was short-circuited above. - * - * For the correctness proof below, decompose the original values of - * A and B as - * A = sa * 2^a * A' with A'=0 or A' odd, and sa = +-1 - * B = sb * 2^b * B' with B'=0 or B' odd, and sb = +-1 - * Then gcd(A, B) = 2^{min(a,b)} * gcd(A',B'), - * and gcd(A',B') is odd or 0. - * - * At the beginning, we have TA = |A| and TB = |B| so gcd(A,B) = gcd(TA,TB). - * The code maintains the following invariant: - * gcd(A,B) = 2^k * gcd(TA,TB) for some k (I) - */ + /* Make boths inputs odd by putting powers of 2 on the side */ + const size_t za = mbedtls_mpi_lsb(&TA); + const size_t zb = mbedtls_mpi_lsb(&TB); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TA, za)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TB, zb)); - /* Proof that the loop terminates: - * At each iteration, either the right-shift by 1 is made on a nonzero - * value and the nonnegative integer bitlen(TA) + bitlen(TB) decreases - * by at least 1, or the right-shift by 1 is made on zero and then - * TA becomes 0 which ends the loop (TB cannot be 0 if it is right-shifted - * since in that case TB is calculated from TB-TA with the condition TB>TA). - */ - while (mbedtls_mpi_cmp_int(&TA, 0) != 0) { - /* Divisions by 2 preserve the invariant (I). */ - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TA, mbedtls_mpi_lsb(&TA))); - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TB, mbedtls_mpi_lsb(&TB))); - - /* Set either TA or TB to |TA-TB|/2. Since TA and TB are both odd, - * TA-TB is even so the division by 2 has an integer result. - * Invariant (I) is preserved since any odd divisor of both TA and TB - * also divides |TA-TB|/2, and any odd divisor of both TA and |TA-TB|/2 - * also divides TB, and any odd divisor of both TB and |TA-TB|/2 also - * divides TA. - */ - if (mbedtls_mpi_cmp_mpi(&TA, &TB) >= 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_abs(&TA, &TA, &TB)); - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TA, 1)); - } else { - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_abs(&TB, &TB, &TA)); - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TB, 1)); - } - /* Note that one of TA or TB is still odd. */ - } + /* Ensure A <= B: if B < A, swap them */ + mbedtls_ct_condition_t swap = mbedtls_mpi_core_lt_ct(TB.p, TA.p, TA.n); + mbedtls_mpi_core_cond_swap(TA.p, TB.p, TA.n, swap); - /* By invariant (I), gcd(A,B) = 2^k * gcd(TA,TB) for some k. - * At the loop exit, TA = 0, so gcd(TA,TB) = TB. - * - If there was at least one loop iteration, then one of TA or TB is odd, - * and TA = 0, so TB is odd and gcd(TA,TB) = gcd(A',B'). In this case, - * lz = min(a,b) so gcd(A,B) = 2^lz * TB. - * - If there was no loop iteration, then A was 0, and gcd(A,B) = B. - * In this case, lz = 0 and B = TB so gcd(A,B) = B = 2^lz * TB as well. - */ + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(G, NULL, &TA, &TB)); - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&TB, lz)); - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(G, &TB)); + /* Re-inject the power of 2 we had previously put aside */ + size_t zg = za > zb ? zb : za; // zg = min(za, zb) + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(G, zg)); cleanup: @@ -1899,91 +1922,139 @@ int mbedtls_mpi_random(mbedtls_mpi *X, } /* - * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + * Modular inverse: X = A^-1 mod N with N odd (and A any range) */ -int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N) +int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *N) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - mbedtls_mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + mbedtls_mpi T, G; - if (mbedtls_mpi_cmp_int(N, 1) <= 0) { - return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + mbedtls_mpi_init(&T); + mbedtls_mpi_init(&G); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, A, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(&G, &T, &T, N)); + if (mbedtls_mpi_cmp_int(&G, 1) != 0) { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; } - mbedtls_mpi_init(&TA); mbedtls_mpi_init(&TU); mbedtls_mpi_init(&U1); mbedtls_mpi_init(&U2); - mbedtls_mpi_init(&G); mbedtls_mpi_init(&TB); mbedtls_mpi_init(&TV); - mbedtls_mpi_init(&V1); mbedtls_mpi_init(&V2); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, &T)); + +cleanup: + mbedtls_mpi_free(&T); + mbedtls_mpi_free(&G); + + return ret; +} + +/* + * Compute X = A^-1 mod N with N even, A odd and 1 < A < N. + * + * This is not obvious because our constant-time modinv function only works with + * an odd modulus, and here the modulus is even. The idea is that computing a + * a^-1 mod b is really just computing the u coefficient in the Bézout relation + * a*u + b*v = 1 (assuming gcd(a,b) = 1, i.e. the inverse exists). But if we know + * one of u, v in this relation then the other is easy to find. So we can + * actually start by computing N^-1 mod A with gives us "the wrong half" of the + * Bézout relation, from which we'll deduce the interesting half A^-1 mod N. + * + * Return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the inverse doesn't exist. + */ +int mbedtls_mpi_inv_mod_even_in_range(mbedtls_mpi *X, + mbedtls_mpi const *A, + mbedtls_mpi const *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi I, G; - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&G, A, N)); + mbedtls_mpi_init(&I); + mbedtls_mpi_init(&G); + /* Set I = N^-1 mod A */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&I, N, A)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(&G, &I, &I, A)); if (mbedtls_mpi_cmp_int(&G, 1) != 0) { ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; goto cleanup; } - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&TA, A, N)); - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TU, &TA)); - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TB, N)); - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TV, N)); + /* We know N * I = 1 + k * A for some k, which we can easily compute + * as k = (N*I - 1) / A (we know there will be no remainder). */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&I, &I, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&I, &I, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(&G, NULL, &I, A)); - MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&U1, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&U2, 0)); - MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&V1, 0)); - MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&V2, 1)); + /* Now we have a Bézout relation N * (previous value of I) - G * A = 1, + * so A^-1 mod N is -G mod N, which is N - G. + * Note that 0 < k < N since 0 < I < A, so G (k) is already in range. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(X, N, &G)); - do { - while ((TU.p[0] & 1) == 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TU, 1)); +cleanup: + mbedtls_mpi_free(&I); + mbedtls_mpi_free(&G); + return ret; +} - if ((U1.p[0] & 1) != 0 || (U2.p[0] & 1) != 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&U1, &U1, &TB)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&U2, &U2, &TA)); - } +/* + * Compute X = A^-1 mod N with N even and A odd (but in any range). + * + * Return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the inverse doesn't exist. + */ +static int mbedtls_mpi_inv_mod_even(mbedtls_mpi *X, + mbedtls_mpi const *A, + mbedtls_mpi const *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi AA; - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&U1, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&U2, 1)); - } + mbedtls_mpi_init(&AA); - while ((TV.p[0] & 1) == 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TV, 1)); + /* Bring A in the range [0, N). */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&AA, A, N)); - if ((V1.p[0] & 1) != 0 || (V2.p[0] & 1) != 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&V1, &V1, &TB)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&V2, &V2, &TA)); - } + /* We know A >= 0 but the next function wants A > 1 */ + int cmp = mbedtls_mpi_cmp_int(&AA, 1); + if (cmp < 0) { // AA == 0 + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + if (cmp == 0) { // AA = 1 + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, 1)); + goto cleanup; + } - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&V1, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&V2, 1)); - } + /* Now we know 1 < A < N, N is even and AA is still odd */ + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod_even_in_range(X, &AA, N)); - if (mbedtls_mpi_cmp_mpi(&TU, &TV) >= 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&TU, &TU, &TV)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&U1, &U1, &V1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&U2, &U2, &V2)); - } else { - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&TV, &TV, &TU)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&V1, &V1, &U1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&V2, &V2, &U2)); - } - } while (mbedtls_mpi_cmp_int(&TU, 0) != 0); +cleanup: + mbedtls_mpi_free(&AA); + return ret; +} - while (mbedtls_mpi_cmp_int(&V1, 0) < 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&V1, &V1, N)); +/* + * Modular inverse: X = A^-1 mod N + * + * Wrapper around mbedtls_mpi_gcd_modinv_odd() that lifts its limitations. + */ +int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N) +{ + if (mbedtls_mpi_cmp_int(N, 1) <= 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; } - while (mbedtls_mpi_cmp_mpi(&V1, N) >= 0) { - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&V1, &V1, N)); + if (mbedtls_mpi_get_bit(N, 0) == 1) { + return mbedtls_mpi_inv_mod_odd(X, A, N); } - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, &V1)); - -cleanup: - - mbedtls_mpi_free(&TA); mbedtls_mpi_free(&TU); mbedtls_mpi_free(&U1); mbedtls_mpi_free(&U2); - mbedtls_mpi_free(&G); mbedtls_mpi_free(&TB); mbedtls_mpi_free(&TV); - mbedtls_mpi_free(&V1); mbedtls_mpi_free(&V2); + if (mbedtls_mpi_get_bit(A, 0) == 1) { + return mbedtls_mpi_inv_mod_even(X, A, N); + } - return ret; + /* If A and N are both even, 2 divides their GCD, so no inverse. */ + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; } #if defined(MBEDTLS_GENPRIME) diff --git a/thirdparty/mbedtls/library/bignum_core.c b/thirdparty/mbedtls/library/bignum_core.c index 88582c2d384b..809169461524 100644 --- a/thirdparty/mbedtls/library/bignum_core.c +++ b/thirdparty/mbedtls/library/bignum_core.c @@ -18,6 +18,7 @@ #include "mbedtls/platform.h" #include "bignum_core.h" +#include "bignum_core_invasive.h" #include "bn_mul.h" #include "constant_time_internal.h" @@ -1019,4 +1020,221 @@ void mbedtls_mpi_core_from_mont_rep(mbedtls_mpi_uint *X, mbedtls_mpi_core_montmul(X, A, &Rinv, 1, N, AN_limbs, mm, T); } +/* + * Compute X = A - B mod N. + * Both A and B must be in [0, N) and so will the output. + */ +static void mpi_core_sub_mod(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_uint *N, + size_t limbs) +{ + mbedtls_mpi_uint c = mbedtls_mpi_core_sub(X, A, B, limbs); + (void) mbedtls_mpi_core_add_if(X, N, limbs, (unsigned) c); +} + +/* + * Divide X by 2 mod N in place, assuming N is odd. + * The input must be in [0, N) and so will the output. + */ +MBEDTLS_STATIC_TESTABLE +void mbedtls_mpi_core_div2_mod_odd(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *N, + size_t limbs) +{ + /* If X is odd, add N to make it even before shifting. */ + unsigned odd = (unsigned) X[0] & 1; + mbedtls_mpi_uint c = mbedtls_mpi_core_add_if(X, N, limbs, odd); + mbedtls_mpi_core_shift_r(X, limbs, 1); + X[limbs - 1] |= c << (biL - 1); +} + +/* + * Constant-time GCD and modular inversion - odd modulus. + * + * Pre-conditions: see public documentation. + * + * See https://www.jstage.jst.go.jp/article/transinf/E106.D/9/E106.D_2022ICP0009/_pdf + * + * The paper gives two computationally equivalent algorithms: Alg 7 (readable) + * and Alg 8 (constant-time). We use a third version that's hopefully both: + * + * u, v = A, N # N is called p in the paper but doesn't have to be prime + * q, r = 0, 1 + * repeat bits(A_limbs + N_limbs) times: + * d = v - u # t1 in Alg 7 + * t1 = (u and v both odd) ? u : d # t1 in Alg 8 + * t2 = (u and v both odd) ? d : (u odd) ? v : u # t2 in Alg 8 + * t2 >>= 1 + * swap = t1 > t2 # similar to s, z in Alg 8 + * u, v = (swap) ? t2, t1 : t1, t2 + * + * d = r - q mod N # t2 in Alg 7 + * t1 = (u and v both odd) ? q : d # t3 in Alg 8 + * t2 = (u and v both odd) ? d : (u odd) ? r : q # t4 Alg 8 + * t2 /= 2 mod N # see below (pre_com) + * q, r = (swap) ? t2, t1 : t1, t2 + * return v, q # v: GCD, see Alg 6; q: no mult by pre_com, see below + * + * The ternary operators in the above pseudo-code need to be realised in a + * constant-time fashion. We use conditional assign for t1, t2 and conditional + * swap for the final update. (Note: the similarity between branches of Alg 7 + * are highlighted in tables 2 and 3 and the surrounding text.) + * + * Also, we re-order operations, grouping things related to the inverse, which + * facilitates making its computation optional, and requires fewer temporaries. + * + * The only actual change from the paper is dropping the trick with pre_com, + * which I think complicates things for no benefit. + * See the comment on the big I != NULL block below for details. + */ +void mbedtls_mpi_core_gcd_modinv_odd(mbedtls_mpi_uint *G, + mbedtls_mpi_uint *I, + const mbedtls_mpi_uint *A, + size_t A_limbs, + const mbedtls_mpi_uint *N, + size_t N_limbs, + mbedtls_mpi_uint *T) +{ + /* GCD and modinv, names common to Alg 7 and Alg 8 */ + mbedtls_mpi_uint *u = T + 0 * N_limbs; + mbedtls_mpi_uint *v = G; + + /* GCD and modinv, my name (t1, t2 from Alg 7) */ + mbedtls_mpi_uint *d = T + 1 * N_limbs; + + /* GCD and modinv, names from Alg 8 (note: t1, t2 from Alg 7 are d above) */ + mbedtls_mpi_uint *t1 = T + 2 * N_limbs; + mbedtls_mpi_uint *t2 = T + 3 * N_limbs; + + /* modinv only, names common to Alg 7 and Alg 8 */ + mbedtls_mpi_uint *q = I; + mbedtls_mpi_uint *r = I != NULL ? T + 4 * N_limbs : NULL; + + /* + * Initial values: + * u, v = A, N + * q, r = 0, 1 + * + * We only write to G (aka v) after reading from inputs (A and N), which + * allows aliasing, except with N when I != NULL, as then we'll be operating + * mod N on q and r later - see the public documentation. + */ + if (A_limbs > N_limbs) { + /* Violating this precondition should not result in memory errors. */ + A_limbs = N_limbs; + } + memcpy(u, A, A_limbs * ciL); + memset((char *) u + A_limbs * ciL, 0, (N_limbs - A_limbs) * ciL); + + /* Avoid possible UB with memcpy when src == dst. */ + if (v != N) { + memcpy(v, N, N_limbs * ciL); + } + + if (I != NULL) { + memset(q, 0, N_limbs * ciL); + + memset(r, 0, N_limbs * ciL); + r[0] = 1; + } + + /* + * At each step, out of u, v, v - u we keep one, shift another, and discard + * the third, then update (u, v) with the ordered result. + * Then we mirror those actions with q, r, r - q mod N. + * + * Loop invariants: + * u <= v (on entry: A <= N) + * GCD(u, v) == GCD(A, N) (on entry: trivial) + * v = A * q mod N (on entry: N = A * 0 mod N) + * u = A * r mod N (on entry: A = A * 1 mod N) + * q, r in [0, N) (on entry: 0, 1) + * + * On exit: + * u = 0 + * v = GCD(A, N) = A * q mod N + * if v == 1 then 1 = A * q mod N ie q is A's inverse mod N + * r = 0 + * + * The exit state is a fixed point of the loop's body. + * Alg 7 and Alg 8 use 2 * bitlen(N) iterations but Theorem 2 (above in the + * paper) says bitlen(A) + bitlen(N) is actually enough. + */ + for (size_t i = 0; i < (A_limbs + N_limbs) * biL; i++) { + /* s, z in Alg 8 - use meaningful names instead */ + mbedtls_ct_condition_t u_odd = mbedtls_ct_bool(u[0] & 1); + mbedtls_ct_condition_t v_odd = mbedtls_ct_bool(v[0] & 1); + + /* Other conditions that will be useful below */ + mbedtls_ct_condition_t u_odd_v_odd = mbedtls_ct_bool_and(u_odd, v_odd); + mbedtls_ct_condition_t v_even = mbedtls_ct_bool_not(v_odd); + mbedtls_ct_condition_t u_odd_v_even = mbedtls_ct_bool_and(u_odd, v_even); + + /* This is called t1 in Alg 7 (no name in Alg 8). + * We know that u <= v so there is no carry */ + (void) mbedtls_mpi_core_sub(d, v, u, N_limbs); + + /* t1 (the thing that's kept) can be d (default) or u (if t2 is d) */ + memcpy(t1, d, N_limbs * ciL); + mbedtls_mpi_core_cond_assign(t1, u, N_limbs, u_odd_v_odd); + + /* t2 (the thing that's shifted) can be u (if even), or v (if even), + * or d (which is even if both u and v were odd) */ + memcpy(t2, u, N_limbs * ciL); + mbedtls_mpi_core_cond_assign(t2, v, N_limbs, u_odd_v_even); + mbedtls_mpi_core_cond_assign(t2, d, N_limbs, u_odd_v_odd); + + mbedtls_mpi_core_shift_r(t2, N_limbs, 1); // t2 is even + + /* Update u, v and re-order them if needed */ + memcpy(u, t1, N_limbs * ciL); + memcpy(v, t2, N_limbs * ciL); + mbedtls_ct_condition_t swap = mbedtls_mpi_core_lt_ct(v, u, N_limbs); + mbedtls_mpi_core_cond_swap(u, v, N_limbs, swap); + + /* Now, if modinv was requested, do the same with q, r, but: + * - decisions still based on u and v (their initial values); + * - operations are now mod N; + * - we re-use t1, t2 for what the paper calls t3, t4 in Alg 8. + * + * Here we slightly diverge from the paper and instead do the obvious + * thing that preserves the invariants involving q and r: mirror + * operations on u and v, ie also divide by 2 here (mod N). + * + * The paper uses a trick where it replaces division by 2 with + * multiplication by 2 here, and compensates in the end by multiplying + * by pre_com, which is probably intended as an optimisation. + * + * However I believe it's not actually an optimisation, since + * constant-time modular multiplication by 2 (left-shift + conditional + * subtract) is just as costly as constant-time modular division by 2 + * (conditional add + right-shift). So, skip it and keep things simple. + */ + if (I != NULL) { + /* This is called t2 in Alg 7 (no name in Alg 8). */ + mpi_core_sub_mod(d, q, r, N, N_limbs); + + /* t3 (the thing that's kept) */ + memcpy(t1, d, N_limbs * ciL); + mbedtls_mpi_core_cond_assign(t1, r, N_limbs, u_odd_v_odd); + + /* t4 (the thing that's shifted) */ + memcpy(t2, r, N_limbs * ciL); + mbedtls_mpi_core_cond_assign(t2, q, N_limbs, u_odd_v_even); + mbedtls_mpi_core_cond_assign(t2, d, N_limbs, u_odd_v_odd); + + mbedtls_mpi_core_div2_mod_odd(t2, N, N_limbs); + + /* Update and possibly swap */ + memcpy(r, t1, N_limbs * ciL); + memcpy(q, t2, N_limbs * ciL); + mbedtls_mpi_core_cond_swap(r, q, N_limbs, swap); + } + } + + /* G and I already hold the correct values by virtue of being aliased */ +} + #endif /* MBEDTLS_BIGNUM_C */ diff --git a/thirdparty/mbedtls/library/bignum_core.h b/thirdparty/mbedtls/library/bignum_core.h index 264ee6355069..f044b33f9389 100644 --- a/thirdparty/mbedtls/library/bignum_core.h +++ b/thirdparty/mbedtls/library/bignum_core.h @@ -822,4 +822,45 @@ void mbedtls_mpi_core_from_mont_rep(mbedtls_mpi_uint *X, mbedtls_mpi_uint mm, mbedtls_mpi_uint *T); +/** Compute GCD(A, N) and optionally the inverse of A mod N if it exists. + * + * Requires N to be odd, 0 <= A <= N and A_limbs <= N_limbs. + * When I != NULL, N (the modulus) must be greater than 1. + * + * A and N may not alias each other. + * When I == NULL (computing only the GCD), G may alias A or N. + * When I != NULL (computing the modular inverse), G or I may alias A + * but none of them may alias N (the modulus). + * + * If any of the above preconditions is not met, output values are unspecified. + * + * \param[out] G The GCD of \p A and \p N. + * Must have the same number of limbs as \p N. + * \param[out] I The inverse of \p A modulo \p N if it exists (that is, + * if \p G above is 1 on exit); indeterminate otherwise. + * This must either be NULL (to only compute the GCD), + * or have the same number of limbs as \p N. + * \param[in] A The 1st operand of GCD and number to invert. + * This value must be less than or equal to \p N. + * \param A_limbs The number of limbs of \p A. + * Must be less than or equal to \p N_limbs. + * \param[in] N The 2nd operand of GCD and modulus for inversion. + * This value must be odd. + * If I != NULL this value must be greater than 1. + * \param N_limbs The number of limbs of \p N. + * \param[in,out] T Temporary storage of size at least 5 * N_limbs limbs, + * or 4 * N_limbs if \p I is NULL (GCD only). + * Its initial content is unused and + * its final content is indeterminate. + * It must not alias or otherwise overlap any of the + * other parameters. + */ +void mbedtls_mpi_core_gcd_modinv_odd(mbedtls_mpi_uint *G, + mbedtls_mpi_uint *I, + const mbedtls_mpi_uint *A, + size_t A_limbs, + const mbedtls_mpi_uint *N, + size_t N_limbs, + mbedtls_mpi_uint *T); + #endif /* MBEDTLS_BIGNUM_CORE_H */ diff --git a/thirdparty/mbedtls/library/bignum_core_invasive.h b/thirdparty/mbedtls/library/bignum_core_invasive.h new file mode 100644 index 000000000000..a9d447f792b0 --- /dev/null +++ b/thirdparty/mbedtls/library/bignum_core_invasive.h @@ -0,0 +1,38 @@ +/** + * \file bignum_core_invasive.h + * + * \brief Function declarations for invasive functions of bignum core. + */ +/** + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#ifndef MBEDTLS_BIGNUM_CORE_INVASIVE_H +#define MBEDTLS_BIGNUM_CORE_INVASIVE_H + +#include "bignum_core.h" + +#if defined(MBEDTLS_TEST_HOOKS) + +#if !defined(MBEDTLS_THREADING_C) + +extern void (*mbedtls_safe_codepath_hook)(void); +extern void (*mbedtls_unsafe_codepath_hook)(void); + +#endif /* !MBEDTLS_THREADING_C */ + +/** Divide X by 2 mod N in place, assuming N is odd. + * + * \param[in,out] X The value to divide by 2 mod \p N. + * \param[in] N The modulus. Must be odd. + * \param[in] limbs The number of limbs in \p X and \p N. + */ +MBEDTLS_STATIC_TESTABLE +void mbedtls_mpi_core_div2_mod_odd(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *N, + size_t limbs); + +#endif /* MBEDTLS_TEST_HOOKS */ + +#endif /* MBEDTLS_BIGNUM_CORE_INVASIVE_H */ diff --git a/thirdparty/mbedtls/library/bignum_internal.h b/thirdparty/mbedtls/library/bignum_internal.h index aceaf55ea2b8..ba1c69d6b1a7 100644 --- a/thirdparty/mbedtls/library/bignum_internal.h +++ b/thirdparty/mbedtls/library/bignum_internal.h @@ -47,4 +47,76 @@ int mbedtls_mpi_exp_mod_unsafe(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *E, const mbedtls_mpi *N, mbedtls_mpi *prec_RR); +/** + * \brief A wrapper around a constant time function to compute + * GCD(A, N) and/or A^-1 mod N if it exists. + * + * \warning Requires N to be odd, and 0 <= A <= N. Additionally, if + * I != NULL, requires N > 1. + * The wrapper part of this function is not constant time. + * + * \note A and N must not alias each other. + * When I == NULL (computing only the GCD), G can alias A or N. + * When I != NULL (computing the modular inverse), G or I can + * alias A, but neither of them can alias N (the modulus). + * + * \param[out] G The GCD of \p A and \p N. + * This may be NULL, to only compute I. + * \param[out] I The inverse of \p A modulo \p N if it exists (that is, + * if \p G above is 1 on exit), in the range [1, \p N); + * indeterminate otherwise. + * This may be NULL, to only compute G. + * \param[in] A The 1st operand of GCD and number to invert. + * This value must be less than or equal to \p N. + * \param[in] N The 2nd operand of GCD and modulus for inversion. + * Must be odd or the results are indeterminate. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if preconditions were not + * met. + */ +int mbedtls_mpi_gcd_modinv_odd(mbedtls_mpi *G, + mbedtls_mpi *I, + const mbedtls_mpi *A, + const mbedtls_mpi *N); + +/** + * \brief Modular inverse: X = A^-1 mod N with N odd + * + * \param[out] X The inverse of \p A modulo \p N in the range [1, \p N) + * on success; indeterminate otherwise. + * \param[in] A The number to invert. + * \param[in] N The modulus. Must be odd and greater than 1. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if preconditions were not + * met. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A is not invertible mod N. + */ +int mbedtls_mpi_inv_mod_odd(mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *N); + +/** + * \brief Modular inverse: X = A^-1 mod N with N even, + * A odd and 1 < A < N. + * + * \param[out] X The inverse of \p A modulo \p N in the range [1, \p N) + * on success; indeterminate otherwise. + * \param[in] A The number to invert. Must be odd, greated than 1 + * and less than \p N. + * \param[in] N The modulus. Must be even and greater than 1. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if preconditions were not + * met. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A is not invertible mod N. + */ +int mbedtls_mpi_inv_mod_even_in_range(mbedtls_mpi *X, + mbedtls_mpi const *A, + mbedtls_mpi const *N); + #endif /* bignum_internal.h */ diff --git a/thirdparty/mbedtls/library/cipher.c b/thirdparty/mbedtls/library/cipher.c index 2ae01dd84d56..f9d46213db98 100644 --- a/thirdparty/mbedtls/library/cipher.c +++ b/thirdparty/mbedtls/library/cipher.c @@ -846,7 +846,8 @@ static void add_pkcs_padding(unsigned char *output, size_t output_len, */ MBEDTLS_STATIC_TESTABLE int mbedtls_get_pkcs_padding(unsigned char *input, size_t input_len, - size_t *data_len) + size_t *data_len, + size_t *invalid_padding) { size_t i, pad_idx; unsigned char padding_len; @@ -872,7 +873,8 @@ MBEDTLS_STATIC_TESTABLE int mbedtls_get_pkcs_padding(unsigned char *input, /* If the padding is invalid, set the output length to 0 */ *data_len = mbedtls_ct_if(bad, 0, input_len - padding_len); - return mbedtls_ct_error_if_else_0(bad, MBEDTLS_ERR_CIPHER_INVALID_PADDING); + *invalid_padding = mbedtls_ct_size_if_else_0(bad, SIZE_MAX); + return 0; } #endif /* MBEDTLS_CIPHER_PADDING_PKCS7 */ @@ -893,7 +895,7 @@ static void add_one_and_zeros_padding(unsigned char *output, } static int get_one_and_zeros_padding(unsigned char *input, size_t input_len, - size_t *data_len) + size_t *data_len, size_t *invalid_padding) { if (NULL == input || NULL == data_len) { return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; @@ -916,7 +918,8 @@ static int get_one_and_zeros_padding(unsigned char *input, size_t input_len, in_padding = mbedtls_ct_bool_and(in_padding, mbedtls_ct_bool_not(is_nonzero)); } - return mbedtls_ct_error_if_else_0(bad, MBEDTLS_ERR_CIPHER_INVALID_PADDING); + *invalid_padding = mbedtls_ct_size_if_else_0(bad, SIZE_MAX); + return 0; } #endif /* MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS */ @@ -937,7 +940,7 @@ static void add_zeros_and_len_padding(unsigned char *output, } static int get_zeros_and_len_padding(unsigned char *input, size_t input_len, - size_t *data_len) + size_t *data_len, size_t *invalid_padding) { size_t i, pad_idx; unsigned char padding_len; @@ -963,7 +966,8 @@ static int get_zeros_and_len_padding(unsigned char *input, size_t input_len, bad = mbedtls_ct_bool_or(bad, nonzero_pad_byte); } - return mbedtls_ct_error_if_else_0(bad, MBEDTLS_ERR_CIPHER_INVALID_PADDING); + *invalid_padding = mbedtls_ct_size_if_else_0(bad, SIZE_MAX); + return 0; } #endif /* MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN */ @@ -978,7 +982,7 @@ static void add_zeros_padding(unsigned char *output, } static int get_zeros_padding(unsigned char *input, size_t input_len, - size_t *data_len) + size_t *data_len, size_t *invalid_padding) { size_t i; mbedtls_ct_condition_t done = MBEDTLS_CT_FALSE, prev_done; @@ -994,6 +998,7 @@ static int get_zeros_padding(unsigned char *input, size_t input_len, *data_len = mbedtls_ct_size_if(mbedtls_ct_bool_ne(done, prev_done), i, *data_len); } + *invalid_padding = 0; return 0; } #endif /* MBEDTLS_CIPHER_PADDING_ZEROS */ @@ -1005,20 +1010,21 @@ static int get_zeros_padding(unsigned char *input, size_t input_len, * but a trivial get_padding function */ static int get_no_padding(unsigned char *input, size_t input_len, - size_t *data_len) + size_t *data_len, size_t *invalid_padding) { if (NULL == input || NULL == data_len) { return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; } *data_len = input_len; - + *invalid_padding = 0; return 0; } #endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ -int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, - unsigned char *output, size_t *olen) +int mbedtls_cipher_finish_padded(mbedtls_cipher_context_t *ctx, + unsigned char *output, size_t *olen, + size_t *invalid_padding) { if (ctx->cipher_info == NULL) { return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; @@ -1034,6 +1040,7 @@ int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, #endif /* MBEDTLS_USE_PSA_CRYPTO && !MBEDTLS_DEPRECATED_REMOVED */ *olen = 0; + *invalid_padding = 0; #if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) /* CBC mode requires padding so we make sure a call to @@ -1110,7 +1117,7 @@ int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, /* Set output size for decryption */ if (MBEDTLS_DECRYPT == ctx->operation) { return ctx->get_padding(output, mbedtls_cipher_get_block_size(ctx), - olen); + olen, invalid_padding); } /* Set output size for encryption */ @@ -1124,6 +1131,19 @@ int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; } +int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, + unsigned char *output, size_t *olen) +{ + size_t invalid_padding = 0; + int ret = mbedtls_cipher_finish_padded(ctx, output, olen, + &invalid_padding); + if (ret == 0) { + ret = mbedtls_ct_error_if_else_0(invalid_padding, + MBEDTLS_ERR_CIPHER_INVALID_PADDING); + } + return ret; +} + #if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) int mbedtls_cipher_set_padding_mode(mbedtls_cipher_context_t *ctx, mbedtls_cipher_padding_t mode) @@ -1393,14 +1413,17 @@ int mbedtls_cipher_crypt(mbedtls_cipher_context_t *ctx, return ret; } - if ((ret = mbedtls_cipher_finish(ctx, output + *olen, - &finish_olen)) != 0) { + size_t invalid_padding = 0; + if ((ret = mbedtls_cipher_finish_padded(ctx, output + *olen, + &finish_olen, + &invalid_padding)) != 0) { return ret; } - *olen += finish_olen; - return 0; + ret = mbedtls_ct_error_if_else_0(invalid_padding, + MBEDTLS_ERR_CIPHER_INVALID_PADDING); + return ret; } #if defined(MBEDTLS_CIPHER_MODE_AEAD) diff --git a/thirdparty/mbedtls/library/cipher_invasive.h b/thirdparty/mbedtls/library/cipher_invasive.h index 702f8f73e9ec..e82a0a7f9951 100644 --- a/thirdparty/mbedtls/library/cipher_invasive.h +++ b/thirdparty/mbedtls/library/cipher_invasive.h @@ -20,7 +20,8 @@ MBEDTLS_STATIC_TESTABLE int mbedtls_get_pkcs_padding(unsigned char *input, size_t input_len, - size_t *data_len); + size_t *data_len, + size_t *invalid_padding); #endif diff --git a/thirdparty/mbedtls/library/dhm.c b/thirdparty/mbedtls/library/dhm.c index bcc07f544194..941a89da80b3 100644 --- a/thirdparty/mbedtls/library/dhm.c +++ b/thirdparty/mbedtls/library/dhm.c @@ -18,6 +18,7 @@ #if defined(MBEDTLS_DHM_C) #include "mbedtls/dhm.h" +#include "bignum_internal.h" #include "mbedtls/platform_util.h" #include "mbedtls/error.h" @@ -344,9 +345,6 @@ static int dhm_update_blinding(mbedtls_dhm_context *ctx, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) { int ret; - mbedtls_mpi R; - - mbedtls_mpi_init(&R); /* * Don't use any blinding the first time a particular X is used, @@ -381,21 +379,11 @@ static int dhm_update_blinding(mbedtls_dhm_context *ctx, /* Vi = random( 2, P-2 ) */ MBEDTLS_MPI_CHK(dhm_random_below(&ctx->Vi, &ctx->P, f_rng, p_rng)); - /* Vf = Vi^-X mod P - * First compute Vi^-1 = R * (R Vi)^-1, (avoiding leaks from inv_mod), - * then elevate to the Xth power. */ - MBEDTLS_MPI_CHK(dhm_random_below(&R, &ctx->P, f_rng, p_rng)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vi, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&ctx->Vf, &ctx->Vf, &ctx->P)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vf, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); - + /* Vf = Vi^-X = (Vi^-1)^X mod P */ + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, &ctx->Vf, &ctx->Vi, &ctx->P)); MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP)); cleanup: - mbedtls_mpi_free(&R); - return ret; } diff --git a/thirdparty/mbedtls/library/ecdsa.c b/thirdparty/mbedtls/library/ecdsa.c index 2f7a996a7e73..a2d1eea6de2e 100644 --- a/thirdparty/mbedtls/library/ecdsa.c +++ b/thirdparty/mbedtls/library/ecdsa.c @@ -17,6 +17,7 @@ #include "mbedtls/ecdsa.h" #include "mbedtls/asn1write.h" +#include "bignum_internal.h" #include @@ -251,7 +252,7 @@ int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, int ret, key_tries, sign_tries; int *p_sign_tries = &sign_tries, *p_key_tries = &key_tries; mbedtls_ecp_point R; - mbedtls_mpi k, e, t; + mbedtls_mpi k, e; mbedtls_mpi *pk = &k, *pr = r; /* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */ @@ -265,7 +266,7 @@ int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, } mbedtls_ecp_point_init(&R); - mbedtls_mpi_init(&k); mbedtls_mpi_init(&e); mbedtls_mpi_init(&t); + mbedtls_mpi_init(&k); mbedtls_mpi_init(&e); ECDSA_RS_ENTER(sig); @@ -340,21 +341,11 @@ int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, MBEDTLS_MPI_CHK(derive_mpi(grp, &e, buf, blen)); /* - * Generate a random value to blind inv_mod in next step, - * avoiding a potential timing leak. - */ - MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, &t, f_rng_blind, - p_rng_blind)); - - /* - * Step 6: compute s = (e + r * d) / k = t (e + rd) / (kt) mod n + * Step 6: compute s = (e + r * d) / k */ MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(s, pr, d)); MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&e, &e, s)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&e, &e, &t)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(pk, pk, &t)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pk, pk, &grp->N)); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(s, pk, &grp->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, s, pk, &grp->N)); MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(s, s, &e)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(s, s, &grp->N)); } while (mbedtls_mpi_cmp_int(s, 0) == 0); @@ -367,7 +358,7 @@ int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, cleanup: mbedtls_ecp_point_free(&R); - mbedtls_mpi_free(&k); mbedtls_mpi_free(&e); mbedtls_mpi_free(&t); + mbedtls_mpi_free(&k); mbedtls_mpi_free(&e); ECDSA_RS_LEAVE(sig); @@ -540,7 +531,7 @@ int mbedtls_ecdsa_verify_restartable(mbedtls_ecp_group *grp, */ ECDSA_BUDGET(MBEDTLS_ECP_OPS_CHK + MBEDTLS_ECP_OPS_INV + 2); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&s_inv, s, &grp->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, &s_inv, s, &grp->N)); MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(pu1, &e, &s_inv)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pu1, pu1, &grp->N)); diff --git a/thirdparty/mbedtls/library/ecp.c b/thirdparty/mbedtls/library/ecp.c index fdd00a59c591..6af516c0ac97 100644 --- a/thirdparty/mbedtls/library/ecp.c +++ b/thirdparty/mbedtls/library/ecp.c @@ -68,6 +68,7 @@ #include "mbedtls/error.h" #include "bn_mul.h" +#include "bignum_internal.h" #include "ecp_invasive.h" #include @@ -1173,7 +1174,7 @@ static inline int mbedtls_mpi_shift_l_mod(const mbedtls_ecp_group *grp, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int_mod(grp, X, A, c)) #define MPI_ECP_INV(dst, src) \ - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod((dst), (src), &grp->P)) + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(NULL, (dst), (src), &grp->P)) #define MPI_ECP_MOV(X, A) \ MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, A)) @@ -2201,21 +2202,6 @@ static int ecp_mul_comb_after_precomp(const mbedtls_ecp_group *grp, final_norm: MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_INV); #endif - /* - * Knowledge of the jacobian coordinates may leak the last few bits of the - * scalar [1], and since our MPI implementation isn't constant-flow, - * inversion (used for coordinate normalization) may leak the full value - * of its input via side-channels [2]. - * - * [1] https://eprint.iacr.org/2003/191 - * [2] https://eprint.iacr.org/2020/055 - * - * Avoid the leak by randomizing coordinates before we normalize them. - */ - if (f_rng != 0) { - MBEDTLS_MPI_CHK(ecp_randomize_jac(grp, RR, f_rng, p_rng)); - } - MBEDTLS_MPI_CHK(ecp_normalize_jac(grp, RR)); #if defined(MBEDTLS_ECP_RESTARTABLE) @@ -2594,18 +2580,6 @@ static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, MPI_ECP_COND_SWAP(&R->Z, &RP.Z, b); } - /* - * Knowledge of the projective coordinates may leak the last few bits of the - * scalar [1], and since our MPI implementation isn't constant-flow, - * inversion (used for coordinate normalization) may leak the full value - * of its input via side-channels [2]. - * - * [1] https://eprint.iacr.org/2003/191 - * [2] https://eprint.iacr.org/2020/055 - * - * Avoid the leak by randomizing coordinates before we normalize them. - */ - MBEDTLS_MPI_CHK(ecp_randomize_mxz(grp, R, f_rng, p_rng)); MBEDTLS_MPI_CHK(ecp_normalize_mxz(grp, R)); cleanup: diff --git a/thirdparty/mbedtls/library/psa_crypto.c b/thirdparty/mbedtls/library/psa_crypto.c index 9c28609d7e9d..9e17e27f2db9 100644 --- a/thirdparty/mbedtls/library/psa_crypto.c +++ b/thirdparty/mbedtls/library/psa_crypto.c @@ -73,6 +73,8 @@ #include "mbedtls/psa_util.h" #include "mbedtls/threading.h" +#include "constant_time_internal.h" + #if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) @@ -1494,8 +1496,8 @@ psa_status_t psa_export_key_internal( key_buffer, key_buffer_size, data, data_size, data_length); } else { - /* This shouldn't happen in the reference implementation, but - it is valid for a special-purpose implementation to omit + /* This shouldn't happen in the built-in implementation, but + it is valid for a special-purpose drivers to omit support for exporting certain key types. */ return PSA_ERROR_NOT_SUPPORTED; } @@ -4692,12 +4694,26 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, output_length); exit: - if (status == PSA_SUCCESS) { - status = psa_cipher_abort(operation); - } else { - *output_length = 0; - (void) psa_cipher_abort(operation); - } + /* C99 doesn't allow a declaration to follow a label */; + psa_status_t abort_status = psa_cipher_abort(operation); + /* Normally abort shouldn't fail unless the operation is in a bad + * state, in which case we'd expect finish to fail with the same error. + * So it doesn't matter much which call's error code we pick when both + * fail. However, in unauthenticated decryption specifically, the + * distinction between PSA_SUCCESS and PSA_ERROR_INVALID_PADDING is + * security-sensitive (risk of a padding oracle attack), so here we + * must not have a code path that depends on the value of status. */ + if (abort_status != PSA_SUCCESS) { + status = abort_status; + } + + /* Set *output_length to 0 if status != PSA_SUCCESS, without + * leaking the value of status through a timing side channel + * (status == PSA_ERROR_INVALID_PADDING is sensitive when doing + * unpadded decryption, due to the risk of padding oracle attack). */ + mbedtls_ct_condition_t success = + mbedtls_ct_bool_not(mbedtls_ct_bool(status)); + *output_length = mbedtls_ct_size_if_else_0(success, *output_length); LOCAL_OUTPUT_FREE(output_external, output); @@ -4841,13 +4857,17 @@ psa_status_t psa_cipher_decrypt(mbedtls_svc_key_id_t key, exit: unlock_status = psa_unregister_read_under_mutex(slot); - if (status == PSA_SUCCESS) { + if (unlock_status != PSA_SUCCESS) { status = unlock_status; } - if (status != PSA_SUCCESS) { - *output_length = 0; - } + /* Set *output_length to 0 if status != PSA_SUCCESS, without + * leaking the value of status through a timing side channel + * (status == PSA_ERROR_INVALID_PADDING is sensitive when doing + * unpadded decryption, due to the risk of padding oracle attack). */ + mbedtls_ct_condition_t success = + mbedtls_ct_bool_not(mbedtls_ct_bool(status)); + *output_length = mbedtls_ct_size_if_else_0(success, *output_length); LOCAL_INPUT_FREE(input_external, input); LOCAL_OUTPUT_FREE(output_external, output); diff --git a/thirdparty/mbedtls/library/psa_crypto_cipher.c b/thirdparty/mbedtls/library/psa_crypto_cipher.c index efc5813ff088..7f691c1d95b9 100644 --- a/thirdparty/mbedtls/library/psa_crypto_cipher.c +++ b/thirdparty/mbedtls/library/psa_crypto_cipher.c @@ -13,6 +13,7 @@ #include "psa_crypto_cipher.h" #include "psa_crypto_core.h" #include "psa_crypto_random_impl.h" +#include "constant_time_internal.h" #include "mbedtls/cipher.h" #include "mbedtls/error.h" @@ -551,7 +552,19 @@ psa_status_t mbedtls_psa_cipher_finish( uint8_t *output, size_t output_size, size_t *output_length) { psa_status_t status = PSA_ERROR_GENERIC_ERROR; - uint8_t temp_output_buffer[MBEDTLS_MAX_BLOCK_LENGTH]; + size_t invalid_padding = 0; + + /* We will copy output_size bytes from temp_output_buffer to the + * output buffer. We can't use *output_length to determine how + * much to copy because we must not leak that value through timing + * when doing decryption with unpadding. But the underlying function + * is not guaranteed to write beyond *output_length. To ensure we don't + * leak the former content of the stack to the caller, wipe that + * former content. */ + uint8_t temp_output_buffer[MBEDTLS_MAX_BLOCK_LENGTH] = { 0 }; + if (output_size > sizeof(temp_output_buffer)) { + output_size = sizeof(temp_output_buffer); + } if (operation->ctx.cipher.unprocessed_len != 0) { if (operation->alg == PSA_ALG_ECB_NO_PADDING || @@ -562,25 +575,34 @@ psa_status_t mbedtls_psa_cipher_finish( } status = mbedtls_to_psa_error( - mbedtls_cipher_finish(&operation->ctx.cipher, - temp_output_buffer, - output_length)); + mbedtls_cipher_finish_padded(&operation->ctx.cipher, + temp_output_buffer, + output_length, + &invalid_padding)); if (status != PSA_SUCCESS) { goto exit; } - if (*output_length == 0) { + if (output_size == 0) { ; /* Nothing to copy. Note that output may be NULL in this case. */ - } else if (output_size >= *output_length) { - memcpy(output, temp_output_buffer, *output_length); } else { - status = PSA_ERROR_BUFFER_TOO_SMALL; + /* Do not use the value of *output_length to determine how much + * to copy. When decrypting a padded cipher, the output length is + * sensitive, and leaking it could allow a padding oracle attack. */ + memcpy(output, temp_output_buffer, output_size); } + status = mbedtls_ct_error_if_else_0(invalid_padding, + PSA_ERROR_INVALID_PADDING); + mbedtls_ct_condition_t buffer_too_small = + mbedtls_ct_uint_lt(output_size, *output_length); + status = mbedtls_ct_error_if(buffer_too_small, + PSA_ERROR_BUFFER_TOO_SMALL, + status); + exit: mbedtls_platform_zeroize(temp_output_buffer, sizeof(temp_output_buffer)); - return status; } @@ -701,17 +723,21 @@ psa_status_t mbedtls_psa_cipher_decrypt( &operation, mbedtls_buffer_offset(output, accumulated_length), output_size - accumulated_length, &olength); - if (status != PSA_SUCCESS) { - goto exit; - } *output_length = accumulated_length + olength; exit: - if (status == PSA_SUCCESS) { - status = mbedtls_psa_cipher_abort(&operation); - } else { - mbedtls_psa_cipher_abort(&operation); + /* C99 doesn't allow a declaration to follow a label */; + psa_status_t abort_status = mbedtls_psa_cipher_abort(&operation); + /* Normally abort shouldn't fail unless the operation is in a bad + * state, in which case we'd expect finish to fail with the same error. + * So it doesn't matter much which call's error code we pick when both + * fail. However, in unauthenticated decryption specifically, the + * distinction between PSA_SUCCESS and PSA_ERROR_INVALID_PADDING is + * security-sensitive (risk of a padding oracle attack), so here we + * must not have a code path that depends on the value of status. */ + if (abort_status != PSA_SUCCESS) { + status = abort_status; } return status; diff --git a/thirdparty/mbedtls/library/psa_crypto_core.h b/thirdparty/mbedtls/library/psa_crypto_core.h index c3c0770142a6..ac92ea2b3704 100644 --- a/thirdparty/mbedtls/library/psa_crypto_core.h +++ b/thirdparty/mbedtls/library/psa_crypto_core.h @@ -24,18 +24,6 @@ #include "mbedtls/threading.h" #endif -/** - * Tell if PSA is ready for this cipher. - * - * \note For now, only checks the state of the driver subsystem, - * not the algorithm. Might do more in the future. - * - * \param cipher_alg The cipher algorithm (ignored for now). - * - * \return 1 if the driver subsytem is ready, 0 otherwise. - */ -int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg); - typedef enum { PSA_SLOT_EMPTY = 0, PSA_SLOT_FILLING, diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c index 557faaf36354..08267dbfce17 100644 --- a/thirdparty/mbedtls/library/rsa.c +++ b/thirdparty/mbedtls/library/rsa.c @@ -1047,7 +1047,7 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, unsigned int nbits, int exponent) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - mbedtls_mpi H, G, L; + mbedtls_mpi H; int prime_quality = 0; /* @@ -1060,8 +1060,6 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, } mbedtls_mpi_init(&H); - mbedtls_mpi_init(&G); - mbedtls_mpi_init(&L); if (exponent < 3 || nbits % 2 != 0) { ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; @@ -1099,35 +1097,28 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, mbedtls_mpi_swap(&ctx->P, &ctx->Q); } - /* Temporarily replace P,Q by P-1, Q-1 */ - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&ctx->P, &ctx->P, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&ctx->Q, &ctx->Q, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&H, &ctx->P, &ctx->Q)); - - /* check GCD( E, (P-1)*(Q-1) ) == 1 (FIPS 186-4 §B.3.1 criterion 2(a)) */ - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&G, &ctx->E, &H)); - if (mbedtls_mpi_cmp_int(&G, 1) != 0) { + /* Compute D = E^-1 mod LCM(P-1, Q-1) (FIPS 186-4 §B.3.1 criterion 3(b)) + * if it exists (FIPS 186-4 §B.3.1 criterion 2(a)) */ + ret = mbedtls_rsa_deduce_private_exponent(&ctx->P, &ctx->Q, &ctx->E, &ctx->D); + if (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + mbedtls_mpi_lset(&ctx->D, 0); /* needed for the next call */ continue; } + if (ret != 0) { + goto cleanup; + } - /* compute smallest possible D = E^-1 mod LCM(P-1, Q-1) (FIPS 186-4 §B.3.1 criterion 3(b)) */ - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&G, &ctx->P, &ctx->Q)); - MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(&L, NULL, &H, &G)); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&ctx->D, &ctx->E, &L)); - - if (mbedtls_mpi_bitlen(&ctx->D) <= ((nbits + 1) / 2)) { // (FIPS 186-4 §B.3.1 criterion 3(a)) + /* (FIPS 186-4 §B.3.1 criterion 3(a)) */ + if (mbedtls_mpi_bitlen(&ctx->D) <= ((nbits + 1) / 2)) { continue; } break; } while (1); - /* Restore P,Q */ - MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&ctx->P, &ctx->P, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&ctx->Q, &ctx->Q, 1)); + /* N = P * Q */ MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->N, &ctx->P, &ctx->Q)); - ctx->len = mbedtls_mpi_size(&ctx->N); #if !defined(MBEDTLS_RSA_NO_CRT) @@ -1146,8 +1137,6 @@ int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, cleanup: mbedtls_mpi_free(&H); - mbedtls_mpi_free(&G); - mbedtls_mpi_free(&L); if (ret != 0) { mbedtls_rsa_free(ctx); @@ -1304,33 +1293,16 @@ static int rsa_prepare_blinding(mbedtls_rsa_context *ctx, } /* Unblinding value: Vf = random number, invertible mod N */ + mbedtls_mpi_lset(&R, 0); do { if (count++ > 10) { ret = MBEDTLS_ERR_RSA_RNG_FAILED; goto cleanup; } - MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&ctx->Vf, ctx->len - 1, f_rng, p_rng)); - - /* Compute Vf^-1 as R * (R Vf)^-1 to avoid leaks from inv_mod. */ - MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&R, ctx->len - 1, f_rng, p_rng)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vf, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->N)); - - /* At this point, Vi is invertible mod N if and only if both Vf and R - * are invertible mod N. If one of them isn't, we don't need to know - * which one, we just loop and choose new values for both of them. - * (Each iteration succeeds with overwhelming probability.) */ - ret = mbedtls_mpi_inv_mod(&ctx->Vi, &ctx->Vi, &ctx->N); - if (ret != 0 && ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { - goto cleanup; - } - - } while (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE); - - /* Finish the computation of Vf^-1 = R * (R Vf)^-1 */ - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vi, &R)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_random(&ctx->Vf, 1, &ctx->N, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(&R, &ctx->Vi, &ctx->Vf, &ctx->N)); + } while (mbedtls_mpi_cmp_int(&R, 1) != 0); /* Blinding value: Vi = Vf^(-e) mod N * (Vi already contains Vf^-1 at this point) */ diff --git a/thirdparty/mbedtls/library/rsa_alt_helpers.c b/thirdparty/mbedtls/library/rsa_alt_helpers.c index 5c265a9921b5..50a5c4e0d74f 100644 --- a/thirdparty/mbedtls/library/rsa_alt_helpers.c +++ b/thirdparty/mbedtls/library/rsa_alt_helpers.c @@ -12,6 +12,7 @@ #include "mbedtls/rsa.h" #include "mbedtls/bignum.h" +#include "bignum_internal.h" #include "rsa_alt_helpers.h" /* @@ -117,7 +118,7 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&K, primes[attempt])); /* Check if gcd(K,N) = 1 */ - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(P, &K, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(P, NULL, &K, N)); if (mbedtls_mpi_cmp_int(P, 1) != 0) { continue; } @@ -136,7 +137,7 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, } MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&K, &K, 1)); - MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(P, &K, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd_modinv_odd(P, NULL, &K, N)); if (mbedtls_mpi_cmp_int(P, 1) == 1 && mbedtls_mpi_cmp_mpi(P, N) == -1) { @@ -197,6 +198,10 @@ int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; } + if (mbedtls_mpi_get_bit(E, 0) != 1) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + mbedtls_mpi_init(&K); mbedtls_mpi_init(&L); @@ -211,8 +216,11 @@ int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, &K, &L)); MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(&K, NULL, &K, D)); - /* Compute modular inverse of E in LCM(P-1, Q-1) */ - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(D, E, &K)); + /* Compute modular inverse of E mod LCM(P-1, Q-1) + * This is FIPS 186-4 §B.3.1 criterion 3(b). + * This will return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if E is not coprime to + * (P-1)(Q-1), also validating FIPS 186-4 §B.3.1 criterion 2(a). */ + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod_even_in_range(D, E, &K)); cleanup: @@ -244,7 +252,7 @@ int mbedtls_rsa_deduce_crt(const mbedtls_mpi *P, const mbedtls_mpi *Q, /* QP = Q^{-1} mod P */ if (QP != NULL) { - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(QP, Q, P)); + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod_odd(QP, Q, P)); } cleanup: diff --git a/thirdparty/mbedtls/library/rsa_alt_helpers.h b/thirdparty/mbedtls/library/rsa_alt_helpers.h index 052b02491e10..8ff74a02bf9c 100644 --- a/thirdparty/mbedtls/library/rsa_alt_helpers.h +++ b/thirdparty/mbedtls/library/rsa_alt_helpers.h @@ -89,12 +89,15 @@ int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, mbedtls_mpi const *E, * \param P First prime factor of RSA modulus * \param Q Second prime factor of RSA modulus * \param E RSA public exponent - * \param D Pointer to MPI holding the private exponent on success. - * - * \return - * - 0 if successful. In this case, D is set to a simultaneous - * modular inverse of E modulo both P-1 and Q-1. - * - A non-zero error code otherwise. + * \param D Pointer to MPI holding the private exponent on success, + * i.e. the modular inverse of E modulo LCM(P-1,Q-1). + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if E is not coprime to P-1 + * and Q-1, that is, if GCD( E, (P-1)*(Q-1) ) != 1. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if inputs are otherwise + * invalid. * * \note This function does not check whether P and Q are primes. * diff --git a/thirdparty/mbedtls/library/ssl_msg.c b/thirdparty/mbedtls/library/ssl_msg.c index 9f50c8e542db..38fd262bc45b 100644 --- a/thirdparty/mbedtls/library/ssl_msg.c +++ b/thirdparty/mbedtls/library/ssl_msg.c @@ -4461,7 +4461,7 @@ static int ssl_load_buffered_message(mbedtls_ssl_context *ssl) ret = 0; goto exit; } else { - MBEDTLS_SSL_DEBUG_MSG(2, ("Next handshake message %u not or only partially bufffered", + MBEDTLS_SSL_DEBUG_MSG(2, ("Next handshake message %u not or only partially buffered", hs->in_msg_seq)); } @@ -6275,7 +6275,7 @@ int mbedtls_ssl_write_early_data(mbedtls_ssl_context *ssl, } else { /* * If we are past the point where we can send early data or we have - * already reached the maximum early data size, return immediatly. + * already reached the maximum early data size, return immediately. * Otherwise, progress the handshake as much as possible to not delay * it too much. If we reach a point where we can still send early data, * then we will send some. diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c index b5bea7521a19..30cde279239b 100644 --- a/thirdparty/mbedtls/library/ssl_tls.c +++ b/thirdparty/mbedtls/library/ssl_tls.c @@ -3627,7 +3627,7 @@ static int ssl_tls12_session_load(mbedtls_ssl_session *session, start = MBEDTLS_GET_UINT64_BE(p, 0); p += 8; - session->start = (time_t) start; + session->start = (mbedtls_time_t) start; #endif /* MBEDTLS_HAVE_TIME */ /* diff --git a/thirdparty/mbedtls/library/ssl_tls12_client.c b/thirdparty/mbedtls/library/ssl_tls12_client.c index 791b84ee39d1..65d6dbd1a760 100644 --- a/thirdparty/mbedtls/library/ssl_tls12_client.c +++ b/thirdparty/mbedtls/library/ssl_tls12_client.c @@ -2024,7 +2024,7 @@ static int ssl_get_ecdh_params_from_cert(mbedtls_ssl_context *ssl) tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id(grp_id); if (tls_id == 0) { - MBEDTLS_SSL_DEBUG_MSG(1, ("ECC group %u not suported", + MBEDTLS_SSL_DEBUG_MSG(1, ("ECC group %u not supported", grp_id)); return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; } diff --git a/thirdparty/mbedtls/library/threading.c b/thirdparty/mbedtls/library/threading.c index fde7cea1d63a..ff8183ed15b2 100644 --- a/thirdparty/mbedtls/library/threading.c +++ b/thirdparty/mbedtls/library/threading.c @@ -17,7 +17,7 @@ #if defined(MBEDTLS_THREADING_C) -#include "mbedtls/threading.h" +#include "threading_internal.h" #if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_PLATFORM_GMTIME_R_ALT) diff --git a/thirdparty/mbedtls/library/threading_internal.h b/thirdparty/mbedtls/library/threading_internal.h new file mode 100644 index 000000000000..21b57c97c868 --- /dev/null +++ b/thirdparty/mbedtls/library/threading_internal.h @@ -0,0 +1,28 @@ +/** + * \file threading_internal.h + * + * \brief Threading interfaces used by the test framework + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#ifndef MBEDTLS_THREADING_INTERNAL_H +#define MBEDTLS_THREADING_INTERNAL_H + +#include "common.h" + +#include + +/* A version number for the internal threading interface. + * This is meant to allow the framework to remain compatible with + * multiple versions, to facilitate transitions. + * + * Conventionally, this is the Mbed TLS version number when the + * threading interface was last changed in a way that may impact the + * test framework, with the lower byte incremented as necessary + * if multiple changes happened between releases. */ +#define MBEDTLS_THREADING_INTERNAL_VERSION 0x03060000 + +#endif /* MBEDTLS_THREADING_INTERNAL_H */ diff --git a/thirdparty/mbedtls/patches/0001-msvc-2019-psa-redeclaration.patch b/thirdparty/mbedtls/patches/0001-msvc-2019-psa-redeclaration.patch index e77cdf476681..736f9ac30780 100644 --- a/thirdparty/mbedtls/patches/0001-msvc-2019-psa-redeclaration.patch +++ b/thirdparty/mbedtls/patches/0001-msvc-2019-psa-redeclaration.patch @@ -1,3 +1,16 @@ +diff --git a/thirdparty/README.md b/thirdparty/README.md +index 49c24897ca..48aab56b70 100644 +--- a/thirdparty/README.md ++++ b/thirdparty/README.md +@@ -654,7 +654,7 @@ File extracted from upstream source: + ## mbedtls + + - Upstream: https://github.com/Mbed-TLS/mbedtls +-- Version: 3.6.4 (c765c831e5c2a0971410692f92f7a81d6ec65ec2, 2025) ++- Version: 3.6.5 (e185d7fd85499c8ce5ca2a54f5cf8fe7dbe3f8df, 2025) + - License: Apache 2.0 + + File extracted from upstream release tarball: diff --git a/thirdparty/mbedtls/include/psa/crypto.h b/thirdparty/mbedtls/include/psa/crypto.h index 2fe9f35ec3..ed7da26276 100644 --- a/thirdparty/mbedtls/include/psa/crypto.h @@ -73,10 +86,10 @@ index 2fe9f35ec3..ed7da26276 100644 /** Set up a key derivation operation. * diff --git a/thirdparty/mbedtls/include/psa/crypto_extra.h b/thirdparty/mbedtls/include/psa/crypto_extra.h -index 70740901e1..e503c9e3ca 100644 +index a710397a77..7a9811bb65 100644 --- a/thirdparty/mbedtls/include/psa/crypto_extra.h +++ b/thirdparty/mbedtls/include/psa/crypto_extra.h -@@ -1164,7 +1164,9 @@ typedef struct psa_pake_cipher_suite_s psa_pake_cipher_suite_t; +@@ -1191,7 +1191,9 @@ typedef struct psa_pake_cipher_suite_s psa_pake_cipher_suite_t; /** Return an initial value for a PAKE cipher suite object. */ @@ -86,7 +99,7 @@ index 70740901e1..e503c9e3ca 100644 /** Retrieve the PAKE algorithm from a PAKE cipher suite. * -@@ -1297,7 +1299,9 @@ typedef struct psa_jpake_computation_stage_s psa_jpake_computation_stage_t; +@@ -1330,7 +1332,9 @@ typedef struct psa_jpake_computation_stage_s psa_jpake_computation_stage_t; /** Return an initial value for a PAKE operation object. */